Java ReentrantReadWriteLocks - 如何在读锁定中安全地获取写锁定?
我目前正在代码中使用一个 ReentrantReadWriteLock 来同步对树状结构的访问。这个结构很大,可以同时由许多线程读取,偶尔会对它的小部分进行修改 - 所以它似乎很适合读写习语。我知道,对于这个特定的类,人们不能将读锁提升为写锁,因此根据Javadocs,必须在获得写锁之前释放读锁。我以前在非重入上下文中成功使用此模式。
然而,我发现我无法可靠地获取写锁定而不会永远阻塞。由于读锁定是可重入的,并且我实际上正在使用它,因此简单的代码
lock.getReadLock().unlock();
lock.getWriteLock().lock()
如果我已重新进入读取锁,则可以阻止。每次调用解锁只会减少保留计数,并且只有在保留计数达到零时才会实际释放锁定。
EDIT来澄清这一点,因为我认为我最初解释得不太好 - 我知道这个类中没有内置的锁升级,我必须简单地释放读锁定并获取写锁定。我的问题是,无论其他线程在做什么,如果它以可重新进入的方式获取它,调用可能不会实际上释放此线程对锁的保持,在这种情况下,对的调用将永远阻塞,因为此线程仍然保持读锁定,因此会阻塞自身。getReadLock().unlock()
getWriteLock().lock()
例如,此代码片段永远不会到达 println 语句,即使以单线程方式运行,没有其他线程访问锁:
final ReadWriteLock lock = new ReentrantReadWriteLock();
lock.getReadLock().lock();
// In real code we would go call other methods that end up calling back and
// thus locking again
lock.getReadLock().lock();
// Now we do some stuff and realise we need to write so try to escalate the
// lock as per the Javadocs and the above description
lock.getReadLock().unlock(); // Does not actually release the lock
lock.getWriteLock().lock(); // Blocks as some thread (this one!) holds read lock
System.out.println("Will never get here");
所以我问,有没有一个很好的成语来处理这种情况?具体来说,当持有读锁定(可能可重入)的线程发现它需要执行一些写入操作,因此想要“挂起”自己的读锁定以拾取写锁定(根据需要在其他线程上阻塞以释放其对读锁定的保留),然后“拾取”其对读锁定的保持之后处于相同状态?
由于此 ReadWriteLock 实现是专门为重入而设计的,那么当可以重入获取读锁定时,肯定有某种明智的方法可以将读锁定提升为写锁定?这是关键部分,意味着幼稚的方法不起作用。