可重入读写锁(Java)- 将写锁嵌套在读锁内

5
在下面的代码片段中,是否会导致死锁?
public class TestLocks {

ReadWriteLock lock = new ReentrantReadWriteLock();
public void add() {
    lock.readLock().lock();
    //code.....

    lock.writeLock().lock();
    //code
    lock.writeLock().unlock();

    //code....
    l.readLock().unlock();
}

我上面所做的是使用ReentrantReadWriteLock,对于读取操作进行加锁'lock',然后再尝试获取写入锁(在释放读取锁之前)。这在生产环境中可能永远不会发生,但我很好奇以上代码是否会导致死锁。


2
这个问题的答案在该类的Javadoc中。 - jtahlborn
@jtahlborn 感谢您的指引(+1)。根据API文档 - http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/ReentrantReadWriteLock.html // 必须在获取写锁之前释放读锁 rwl.readLock().unlock(); rwl.writeLock().lock(); 因此,在获取写锁之前必须释放读锁。文档还指出: “此外,编写者可以获取读锁,但反之则不行。” 然而,在我上面发布的代码片段中(尝试在仅具有读锁时获取WriteLock),编译器从未给出任何异常。 - srock
1
编译器不会抛出异常。你是指(a)编译器从未打印过编译错误吗?如果是这样,你期望出现什么错误?还是你是指(b)JVM从未抛出过异常?如果是这样,你期望抛出什么异常? - user207421
同意你所说的。+1 - srock
1个回答

9
public void add() {
    lock.readLock().lock();
    //code.....

    lock.writeLock().lock();
    //code
    lock.writeLock().unlock();

    //code....
    l.readLock().unlock();
}

考虑如果两个线程都运行这段代码。在第一行之后,它们都拥有读锁。现在,它们都调用writeLock。除非获取写锁,否则任何线程都不会释放其读锁。但是,直到另一个线程释放其读锁,任何线程都无法获取写锁。因此它们可能发生死锁。

1
为什么它们“可能会死锁”,难道上面的代码不会一直陷入死锁吗?此外,假设只有一个正在运行的线程,它也会被阻塞,即线程首先获取读锁,然后尝试获取写锁,但由于读锁已经获得了锁,因此程序将在那里停止(循环依赖?),即使只有一个线程。 - srock
1
再次强调,这一切都有记录。"无法从读锁升级到写锁。" - user207421
@srock 的意思是,无论锁的实现细节如何,这段代码都不可能避免死锁。你可以推理出没有读写锁能够使其正常工作。 - David Schwartz
1
那反过来呢?先执行 lock.writeLock().lock() 再执行 lock.readLock().lock()?会不会出现死锁的情况? - du369
3
可实现安全的降级锁定。 - David Schwartz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接