我现在在代码中使用ReentrantReadWriteLock来同步树状结构的访问。这种结构很大,可以被许多线程同时读取,偶尔对其一部分进行修改,因此似乎很适合读写习惯。我知道对于这一特定类,不能将读取锁提升为写入锁,因此根据Javadocs,必须在获得写入锁之前释放读取锁。之前,我已经在非可重入上下文中成功使用了这种模式。
但是,我发现我无法可靠地获取写锁而不会永远阻塞。由于读锁是可重入的,而我实际上是这样使用的,因此简单的代码
lock.getReadLock().unlock();
lock.getWriteLock().lock()
如果我重新获得了readlock,则可以阻止。每次解锁请求只会减少保留计数,并且只有在保留计数达到零时才实际释放锁定。
编辑以澄清这一点,因为我认为我一开始解释得不太好-我知道此类中没有内置的锁升级,并且我必须简单地释放读取锁并获得写入锁。我的问题是/不管其他线程在做什么,如果重新获得了该调用,则调用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实现是专门设计为可重入的,因此,当可以重入获取锁时,肯定有某种明智的方法可以将读锁提升为写锁?这是至关重要的部分,这意味着幼稚的方法行不通。