I am currently using ReentrantReadWriteLock in my code to synchronize access to the tree structure. This structure is large and read by many threads at the same time as random changes in its small parts - so it seems to be well suited to the read-write idiom. I understand that with this particular class it is not possible to lock a read lock to lock a write, as in Javadocs it is necessary to release a read lock before receiving a write lock. I have used this template successfully in non-reentrant contexts before.
However, I found that I cannot reliably get a write lock without locking forever. Since the read lock is reentrant and I actually use it as such, simple code
lock.getReadLock().unlock(); lock.getWriteLock().lock()
may be blocked if I purchased readlock again. Each unlock call simply reduces the number of holds, and the lock is actually released only when the number of samples reaches zero.
EDIT to clarify this, since I don’t think I explained it too well initially - I know that in this class there is no built-in escalation of the lock, and that I just need to release the read lock and get a write lock. My problem is that no matter what other threads do, a call to getReadLock().unlock() may not actually release this thread when it locks, if it acquired it again, in which case a call to getWriteLock().lock() will be blocked forever, since this thread still holds a read lock and thereby blocks itself.
For example, this piece of code will never reach the println statement, even if you run singlethreaded without any other threads accessing the lock:
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");
So, I ask, is there a nice idiom to handle this situation? In particular, when a stream containing a read lock (possibly repeatedly) discovers that it needs to perform some writing, and thus wants to “pause” its own read lock in order to obtain a write lock (lock, as required for other threads to let go of your tricks in reading lock), and then "raise" its commit to lock reading in the same state after that?
Since this implementation of ReadWriteLock was specifically designed to be reentrant, is there really any reasonable way to increase read locks to write locks, when locks can be re-acquired? This is the critical part, which means that the naive approach does not work.
java concurrency reentrantreadwritelock
Andrzej Doyle Jan 21 '09 at 10:42 2009-01-21 10:42
source share