考虑这个JDK
标准接口:
public interface ReadWriteLock{
public Lock readLock();
public Lock writeLock();
}
B. Java中的Goetz实际上并发提到从readLock升级到writeLock是容易死锁的。
如果两个读取器同时尝试升级到写锁定,则两者都不会释放读锁定。
令我困惑的是,有两位读者试图升级。但即便是一位读者就够了,不是吗?如果读者试图升级它还没有释放读锁定。尝试使用读锁定获取写锁是死锁的。
因此,我认为提供升级操作在理论上甚至是荒谬的。或者也许实现可以解决这个问题?
听起来你正在考虑将“读锁定”和“写锁定”作为组成读写锁的两个不同的锁。这不是思考它的正确方法,即使API似乎暴露了这样的组合(通过提供获取对“写锁定”和“读锁定”的引用的方法)。
(这一点都被“锁定”一词的重载所迷惑 - 它既是动词又是名词,也就是说,人们可以锁定锁定。
而不是认为ReadWriteLock
的readLock
方法和writeLock
方法返回实际锁定,考虑他们实际做的是返回一个抽象机制,允许获取不同类型的锁(在相同的,单个,读写锁机制)。
读写锁(机制)是单锁,可以通过两种方式锁定:读锁定和写锁定。读写锁可以由一个或多个线程同时读取锁定,也可以是写锁定的。它永远不能同时读取和写入锁定。
如果读者试图升级它还没有释放读锁定。尝试使用读锁定获取写锁是死锁的。
写锁定比读锁定更强;它就像一个带有附加属性的读锁(没有其他线程也可以持有锁)。从读锁定升级到写锁定不是在另一个锁定已经被保持时获取一个锁定;相反,它是关于改变已经锁定在一个锁上的锁的类型。
因此,单个线程将其读锁升级为写锁没有概念问题。它只需要等到读锁的所有其他持有者在升级之前放弃它。在这种情况下不存在死锁的可能性。
例如,假设有三个线程--A,B和C,它们都具有读锁定锁。线程A尝试升级到写锁定。这意味着它必须等待B和C放弃它们的读锁定。这最终会发生,并且在那时线程A获得写锁(并且最终,线程A将放弃此锁)。
另一方面,考虑A和B是否都尝试升级到写锁定。这意味着他们都在等待另一个放弃读锁定,这不会发生;对于线程A放弃读锁定,它首先需要获取写锁定,直到线程B放弃读锁定才会发生写锁定,直到它获得写锁定才会执行,这不会发生直到线程A放弃读锁定......等等。有一个僵局。
Java API不允许从读锁升级到写锁,因为这可以防止死锁情况。编写一个可以安全地升级锁类型的程序是可能的(如果有点棘手),但Java API不允许它。