我目前在我的代码中使用ReentrantReadWriteLock来同步对树状结构的访问。这个结构很大,可以同时被许多线程读取,偶尔会修改其中的一小部分——所以它似乎很适合读写习惯。我知道对于这个特定的类,不能将读锁提升为写锁,因此根据 Javadocs,必须在获得写锁之前释放读锁。我以前在不可重入上下文中成功地使用过这种模式。
然而,我发现我无法在不永久阻塞的情况下可靠地获取写锁。由于读锁是可重入的,我实际上是这样使用它的,所以简单的代码
lock.getReadLock().unlock();
lock.getWriteLock().lock()
如果我以可重入方式获得了读锁,则可以阻止。每次解锁调用只会减少保持计数,并且只有在保持计数达到零时才会真正释放锁。
编辑澄清这一点,因为我认为我最初解释得不太好 - 我知道这个类中没有内置的锁升级,我必须简单地释放读锁并获得写锁。我的问题是/是,无论其他线程在做什么,调用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 实现是专门设计为可重入的,当可以重入获取锁时,肯定有一些明智的方法可以将读锁提升为写锁吗?这是关键部分,这意味着天真的方法不起作用。