读写锁。理解从读锁升级到写锁

3

考虑这个 JDK 标准接口:

public interface ReadWriteLock{
    public Lock readLock();
    public Lock writeLock();
}

B. Goetz在《Java并发编程实战》中提到,从readLock升级到writeLock容易导致死锁。

如果两个读取者同时尝试升级为写入锁,它们都不会释放读取锁。

让我感到困惑的是,即使只有一个读取者尝试升级,情况也是如此。如果一个读取者尝试升级,则尚未释放读取锁。持有读取锁的情况下尝试获取写入锁容易导致死锁。

因此,根据这一点,我认为提供升级操作甚至在理论上也是没有意义的。或者也许可以通过一些实现来解决这个问题?


“在持有读锁的情况下尝试获取写锁会导致死锁” - 你这句话是什么意思?为什么你认为会出现这种情况? - davmac
1个回答

8
看起来你认为“读锁”和“写锁”是两个不同的锁,它们组成了读写锁。然而这并不是正确的思考方式,尽管 API 看起来提供了获取“写锁”和“读锁”引用的方法,以暴露这样的组合。
(这个问题有点混乱,因为术语“锁”被重载了,既是一个动词,也是一个名词,也就是说,可以锁定一个锁。)
与其认为 ReadWriteLock 的 readLock 方法和 writeLock 方法返回实际的锁,不如将它们视为返回一个抽象机制,允许对相同的单个读写锁机制进行不同类型的锁定。
读写锁(机制)是一种单一的锁,可以通过两种方式进行锁定:读锁定和写锁定。读写锁可以由一个或多个线程同时进行读锁定,或者可以进行写锁定。它永远不能同时进行读锁定和写锁定。
如果一个读取器尝试升级且尚未释放读锁,则在保持读锁的情况下尝试获取写锁会导致死锁。
写锁比读锁更强,它类似于具有额外属性的读锁(即没有其他线程可以持有锁)。从读锁定升级到写锁定并不是当已经持有另一个锁时获得一个锁,而是更改已在单个锁上持有的锁类型。
因此,单个线程升级其读锁为写锁不存在概念上的问题;只需要等待所有其他读锁持有者放弃持有的锁,然后才能进行升级。在这种情况下,不存在死锁的可能性。
例如,假设有三个线程 A、B 和 C,它们都对锁进行了读锁定。线程 A 尝试升级为写锁。这意味着它必须等待 B 和 C 放弃它们的读锁。最终会发生这种情况,此时线程 A 获取了写锁(最终,线程 A 将放弃此锁)。
另一方面,如果 A 和 B 都尝试升级为写锁,则它们都在等待彼此放弃读锁,这是不可能的;要使线程 A 放弃读取锁,它首先需要获取写锁,这将不会发生,直到线程 B 放弃读取锁,但线程 B 不会这样做,直到它获得写锁,这将不会发生,直到线程 A 放弃读取锁……以此类推。这就是死锁。
Java API不允许从读锁升级到写锁,因为这会导致死锁。虽然有可能编写一个能够安全地升级锁类型的程序(尽管有些棘手),但Java API无论如何都不允许这样做。

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