对于特定的线程安全数据结构,我需要保护对*数据结构(即字节数组)的访问.在这种情况下,我选择使用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保护的数据的情况.在这种情况下,锁的手动实现会失去其性能.