听起来您正在考虑将“读锁”和“写锁”视为构成读写锁的两个不同的锁。这不是正确的思考方式,尽管 API 似乎公开了这样的组合(通过提供方法来获取对“写锁”和“读锁”的引用)。
(这一切都因术语“锁”的重载而有点混乱——它既是verb and a noun,也就是说,可以lock a lock).
而不是这样想ReadWriteLock
's readLock
方法和writeLock
方法返回实际的锁,考虑它们实际做的是返回一个抽象机制允许获取不同类型的锁(在same、单一、读写锁机制)。
读写锁(机制)是一个single锁可以通过两种方式锁定:读锁定 and 写锁定。读写锁可以是读锁定由一个或多个线程同时执行,或者可以是写锁定。它永远不能同时被读锁定和写锁定。
如果读取器尝试升级,则它尚未释放读取锁定。尝试在持有读锁的情况下获取写锁会导致死锁。
写锁比读锁更强;它就像具有附加属性的读锁(没有其他线程也可以持有该锁)。从读锁升级到写锁并不是在已经持有另一个锁的情况下获取另一个锁;而是要获取另一个锁。相反,它是关于更改单个锁上已经持有的锁的类型。
所以,单线程将读锁升级为写锁不存在概念上的问题;它只需要等待读锁的所有其他持有者放弃它即可进行升级。在这种情况下不存在死锁的可能性。
例如,假设有三个线程 - A、B 和 C,它们都对锁进行了读锁定。线程 A 尝试升级到写锁。这意味着它必须等待 B 和 C 放弃读锁。这最终会发生,此时线程 A 获取写锁(最终,线程 A 将放弃该锁)。
另一方面,考虑 A 和 B 是否都尝试升级到写锁。这意味着它们都在等待对方放弃读锁,但这种情况不会发生;为了让线程 A 放弃读锁,它首先需要获取写锁,这不会发生,直到线程 B 放弃读锁,它才会这样做,直到它获取写锁,这不会发生直到线程 A 放弃读锁......等等。出现僵局。
Java API 不允许从读锁升级到写锁,因为这可以防止死锁情况。编写一个可以安全升级锁类型的程序是可能的(如果有点棘手),但 Java API 无论如何都不允许这样做。