假设我有两个关键资源,foo和bar。我用一些ReentrantReadWriteLock保护他们
ReentrantReadWriteLock foo = new RRWL() ...
ReentrantReadWriteLock bar = new RRWL() ...大多数操作只使用foo或bar,但其中一些恰好两者都使用。现在,当使用单个锁时,您不能这样做:
void foo() {
foo.writeLock().lock();
privateWorkOnFoo();
foo.writeLock().unlock();
}如果抛出一个异常,你的foo将永远被锁定。相反,你把它包装起来,就像
void foo() {
try {
foo.writeLock().lock();
privateWorkOnFoo();
} finally { foo.writeLock().unlock(); }
}但是,如果我需要同时处理这两个问题呢?把它们放在一个区块里安全吗?
选项1
try {
foo.writeLock().lock();
bar.writeLock().lock();
magic();
} finally {
bar.writeLock().unlock();
foo.writeLock().unlock();
}或者,是否有必要为每个锁提供自己的块:
选项2
try {
foo.writeLock().lock();
try {
bar.writeLock().lock();
magic();
} finally {
bar.writeLock().unlock();
}
} finally {
foo.writeLock().unlock();
}我不可能是第一个有困难调查此事的人...我知道选项2是“防弹的”,但它也需要大量的维护。选项1可以接受吗?
发布于 2013-05-05 15:07:49
选项1很好。它被称为双锁变体。如果您查看LinkedBlockingQueue操作,例如remove,它将锁定putLock和takeLock。以下是JDK的功能示例:
public boolean remove(Object o) {
if (o == null) return false;
fullyLock();
try
{
// ...
}
finally {
fullyUnlock();
}
}
/**
* Lock to prevent both puts and takes.
*/
void fullyLock() {
putLock.lock();
takeLock.lock();
}
/**
* Unlock to allow both puts and takes.
*/
void fullyUnlock() {
takeLock.unlock();
putLock.unlock();
}发布于 2013-05-05 15:09:35
选项1实际上比选项2更安全,因为如果在选项2中抛出异常,第二个锁(foo)将不会被解锁:解锁不在finally块中。
此外,在操作两个锁时要非常小心,因为如果一个线程先锁定foo,然后锁定bar,而另一个线程先锁定bar,然后再锁定foo,则很有可能发生死锁。
发布于 2013-05-05 15:18:34
根据Lock API,lock()和unlock()方法都可能抛出异常。因此版本1是不正确的,因为第二次解锁可能永远不会被调用。版本2也是不正确的,您不应该在try块中调用lock(),因为如果lock.lock()抛出异常,则它没有被锁定,并且您不应该尝试解锁它。
正确的版本应该是这样的
foo.writeLock().lock();
try {
bar.writeLock().lock();
try {
magic();
} finally {
bar.writeLock().unlock();
}
} finally {
foo.writeLock().unlock();
}https://stackoverflow.com/questions/16382193
复制相似问题