java – 多个条件与多个锁

对于特定的线程安全数据结构,我需要保护对*数据结构(即字节数组)的访问.在这种情况下,我选择使用ReentrantLocks,因为它的公平性政策以及创建多个条件的高级功能.

并发的条件很复杂,如下所示:

>中心字节数组必须独占保护(即一次一个线程).
>两个访问方法(foo和bar)必须能够并发运行(如果它们尝试访问中心字节数组,则在内部阻塞).
>对任何方法(foo和bar)的调用都需要是独占的(即从不同的线程多次调用foo将导致一个线程阻塞).

在我最初的实现中,我选择实现两个嵌套锁,如下所示:

ReentrantLock lockFoo = new ReentrantLock(true);
ReentrantLock lockCentral = new ReentrantLock(true);

Condition centralCondition = lockCentral.newCondition();

public void foo(){
    // thread-safe processing code here

    lockFoo.lock();        
    lockCentral.lock();

    try{
        // accessing code here

        try{
            // waits upon some condition for access
            while(someCondition){
                centralCondition.await();
            }
        }catch(InterruptedException ex){
            // handling code here
        }

        // more processing
    }finally{
        lockCentral.unlock();
        lockFoo.unlock();
    }
}

结构在方法栏中是等效的,只需用另一个锁定对象lockBar即可.此外,代码减少了我更复杂的多条件等待,并为简单起见信号到单一条件.

使用这个,我不禁感到代码似乎不必要地复杂和模糊不仅因为嵌套有两个锁,它们共享一个try-finally,更不用说lockCoo是多次释放和重新获取lockCentral贯穿始终.

相反,我尝试重新组织外部锁(lockFoo和lockBar)作为lockCentral的条件,如下所示:

ReentrantLock lockCentral = new ReentrantLock(true);

Condition fooCondition = lockCentral.newCondition();
Condition centralCondition = lockCentral.newCondition();

boolean isInFoo = false;

public void foo(){
    // thread-safe processing code here

    lockCentral.lock();

    try{
        // implement method exclusiveness via fooCondition

        try{
            while(isInFoo){
                fooCondition.await();
            }

            isInFoo = true;
        }catch(InterruptedException ex){
            return;
        }

        // accessing code here

        try{
            // waits upon some condition for access
            while(someCondition){
                centralCondition.await();
            }
        }catch(InterruptedException ex){
            // handling code here
        }

        // more processing
    }finally{
        isInFoo = false;
        fooCondition.signal();

        lockCentral.unlock();
    }
}

在一些检查之后,我无法决定前者是更好的想法还是后者(特别是包含那个随机布尔值).简化代码的想法似乎导致了更长的代码,在这种情况下非常违反直觉.

有没有一些惯例或令人信服的理由来论证:

>每个锁定上下文使用一个锁(前一个代码,其中锁定的不同原因共享不同的锁).
>每个锁定资源使用一个锁(后一个代码,其中要保护的中心结构使用单个锁,其他所有其他实现为访问所述结构的条件).

解决方法:

后者代码与前者的不同之处仅在于使用fooCondition手动实现锁定lockFoo(对于条形相关部分也是如此).

因为这样的锁实现考虑到了,那个foo临界区几乎与中心区相同,所以如果没有foo()上的争用(在这种情况下从不执行等待fooCondition),则保证更快.

除了性能原因之外,以前的代码是可取的,因为它是自我记录的.此外,它可以扩展到需要在没有lockCentral的情况下访问受lockFoo保护的数据的情况.在这种情况下,锁的手动实现会失去其性能.

上一篇:java – 多线程:为什么输出这个?这是确定性的吗?


下一篇:Java-并发清除列表