可重入读写锁 - 为什么读者无法获取写者的锁?

4
ReentrantReadWriteLock文档中,它说:
writer can acquire the read lock, but not vice-versa

如果我理解正确,它的意思是你可以从同一个线程中执行以下操作:
//thread1
lock.writeLock().lock()
lock.readLock().lock()
print("this line executes")

这是有道理的:如果您已经锁定了write,则没有其他线程可以进入锁定代码。但是,如果您锁定了read,为什么如果没有其他线程进行read锁定,同一线程不能进入write块呢?所以这行不通:
//thread1
lock.readLock().lock()
lock.writeLock().lock()
print("this line doesn't execute")

为什么在同一线程中锁定写入之前必须解锁读取

如果您打算读取和写入,那么您不需要同时获取读取锁和写入锁。只需获取写入锁即可实现此操作。同样地,一旦获取了读取锁,就不能获取写入锁,直到释放了读取锁。 - Michael
@Michael:“如果您打算读取和写入,则不需要获取读取和写入锁。”这是正确的,但有时您希望从持有写入锁转换为持有读取锁,并且确实以这种方式进行转换,即在仍持有写入锁的情况下获取读取锁,然后释放写入锁。 - Tillmann
2个回答

5

ReentrantReadWriteLock并不意味着不遵循常规的锁定规则。

如果一个线程为了读取目标数据而获取了一个锁,它期望目标数据的值在锁定期间不会改变。否则会导致无法重复读取。从概念上讲,如果您允许一个线程(同一个或另一个)在读取锁已经被获取时获取写入锁定,那么您正在违反该规则。

如果一个线程获取了一个写入锁,那么它隐含地拥有了阅读权限,因此可以授予获取读锁定的权限,因为这并不真正违反锁定的约定(如果我可以写入,那么我也可以读取),也不违反锁定持有者的期望(我为了写入而锁定,所以现在只有我能够读取或写入)。


3
当您已经拥有读锁时,获取写锁并不一定违反任何规则。这叫做“升级”,一些读写库允许它。当线程A和B都有读权限,而线程A尝试升级时,该库应该阻止其他线程获得读权限,并且必须阻塞线程A直到线程B放弃读权限。缺点是,如果线程B尝试升级,则会出现死锁。 ReadWriteLock的Javadoc明确允许实现以任一方式工作,但ReentrantReadWriteLock的作者选择不允许升级。 - Solomon Slow

0

我实际上不知道答案,但它可能是为了帮助您避免编写可能导致死锁的代码。

假设您有两个执行相同代码的线程。这两个线程都获得了读锁。然后,这两个线程都尝试获取写锁。没有一个线程能够继续执行,直到另一个线程释放其读锁,但当等待升级时,没有一个线程会释放其读锁。

不同的实现可以检测到死锁,并在发现死锁时在两个线程中抛出异常,但也许有人认为这会对不需要死锁检测的应用程序产生负面影响,或者会使API过于复杂。

通过禁止您将读锁升级为写锁,他们使您无法编写以那种特定方式死锁的程序。


“我其实不知道答案……”-- 如果你不知道答案,请不要发布回答。StackOverflow不是一个讨论网站,答案应该是真正的答案,而不仅仅是猜测(顺便说一下,是错误的)。SO的整个目的是作为一个好问题和答案的存储库,供未来的读者使用。解决OP的问题是次要的,猜测只会污染网站。 - Jim Garrison
@Jim,“(顺便说一下,是错误的)”,请解释一下为什么您认为Solomon的猜测是错误的。 - Tillmann

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