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