java - 读写锁。了解从 readLock 升级到 writeLock

标签 java multithreading readwritelock

考虑这个 JDK 标准接口(interface):

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

B. Goetz 在 Java Concurrency in practice 中提到从 readLock 升级到 writeLock 容易死锁。

If two readers simultaneously attempt to upgrade to a write lock, neither will realese the read lock.

让我感到困惑的是,两个 读者试图升级。但即使是一个读者就足够了,不是吗?如果读者尝试升级它还没有释放读锁。在持有读锁的情况下尝试获取写锁是死锁。

因此,据此判断,我认为提供升级操作在理论上什至是荒谬的。或者也许实现可以解决这个问题?

最佳答案

听起来您正在考虑将“读锁”和“写锁”视为构成读写锁的两个不同的锁。这不是正确的思考方式,即使 API 似乎公开了这样的组合(通过提供方法来获取对“写锁”和“读锁”的引用)。

(这一切都被术语“锁”的重载所混淆 - 它既是一个动词又是一个名词,也就是说,一个人可以一个)。

与其认为 ReadWriteLockreadLock 方法和 writeLock 方法返回实际的锁,不如考虑它们实际上做的是返回一个抽象机制 允许获取不同类型的锁(在相同、单一、读写锁机制上)。

读写锁(机制)是一个锁,可以通过两种方式锁定:读锁写锁。读写锁可以同时被一个或多个线程读锁,也可以写锁。它永远不能同时被读和写锁定。

If a reader tries to upgrade it's not released the read lock yet. Trying to acquire the write lock with the read lock held is deadlocky.

写锁比读锁强;它就像一个具有附加属性的读锁(没有其他线程也可能持有该锁)。从读锁升级到写锁并不是在已经持有另一个锁的情况下获取一个锁;相反,它是关于更改已经持有在单个锁上的锁的类型。

因此,单线程将读锁升级为写锁不存在概念上的问题;它只需要等到读锁的所有其他持有者在升级发生之前放弃它。在这种情况下不存在死锁的可能性。

例如,假设有三个线程——A、B、C,都对锁进行了读锁。线程 A 尝试升级到写锁。这意味着它必须等待 B 和 C 放弃它们的读锁。这最终会发生,此时线程 A 获得一个写锁(并且,最终,线程 A 将放弃这个锁)。

另一方面,考虑A和B是否都尝试升级为写锁。这意味着他们都在等待对方放弃读锁,这不会发生;对于线程 A 放弃读锁,它首先需要获取写锁,直到线程 B 放弃读锁,它才会这样做,直到它获得写锁,这不会发生直到线程 A 放弃读锁……等等。出现死锁。

Java API 不允许从读锁升级到写锁,因为这样可以防止出现死锁情况。编写一个可以安全升级锁类型的程序是可能的(如果有点棘手的话),但无论如何 Java API 都不允许这样做。

关于java - 读写锁。了解从 readLock 升级到 writeLock,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37087120/

相关文章:

c++ - 如何从更基本的同步原语制作多读/单写锁?

c++ - 如何在 C++14 中实现读/写锁

java - Spring Security OAuth2 client_credentials 仅流

Java 网页开发 : Sessions are not saved between requests and a new JSESSIONID is created

C++ REST SDK : asynchronous tasks vs. C++11 多线程

java - Java 线程库

java - JSF 中的请求范围

java - 在 Google Cloud End Points 中一次插入多个相同类型的记录

java - 我如何使用一个 Java 程序来监视另一个 Java 程序的输出?

c++ - 此用例是否需要读写锁