I am using in my code at the moment a ReentrantReadWriteLock to synchronize access over a tree-like structure. This structure is large, and read by many threads at once wit
What you want to do ought to be possible. The problem is that Java does not provide an implementation that can upgrade read locks to write locks. Specifically, the javadoc ReentrantReadWriteLock says it does not allow an upgrade from read lock to write lock.
In any case, Jakob Jenkov describes how to implement it. See http://tutorials.jenkov.com/java-concurrency/read-write-locks.html#upgrade for details.
An upgrade from read to write lock is valid (despite the assertions to the contrary in other answers). A deadlock can occur, and so part of the implementation is code to recognize deadlocks and break them by throwing an exception in a thread to break the deadlock. That means that as part of your transaction, you must handle the DeadlockException, e.g., by doing the work over again. A typical pattern is:
boolean repeat;
do {
repeat = false;
try {
readSomeStuff();
writeSomeStuff();
maybeReadSomeMoreStuff();
} catch (DeadlockException) {
repeat = true;
}
} while (repeat);
Without this ability, the only way to implement a serializable transaction that reads a bunch of data consistently and then writes something based on what was read is to anticipate that writing will be necessary before you begin, and therefore obtain WRITE locks on all data that are read before writing what needs to be written. This is a KLUDGE that Oracle uses (SELECT FOR UPDATE ...). Furthermore, it actually reduces concurrency because nobody else can read or write any of the data while the transaction is running!
In particular, releasing the read lock before obtaining the write lock will produce inconsistent results. Consider:
int x = someMethod();
y.writeLock().lock();
y.setValue(x);
y.writeLock().unlock();
You have to know whether someMethod(), or any method it calls, creates a reentrant read lock on y! Suppose you know it does. Then if you release the read lock first:
int x = someMethod();
y.readLock().unlock();
// problem here!
y.writeLock().lock();
y.setValue(x);
y.writeLock().unlock();
another thread may change y after you release its read lock, and before you obtain the write lock on it. So y's value will not be equal to x.
import java.util.*;
import java.util.concurrent.locks.*;
public class UpgradeTest {
public static void main(String[] args)
{
System.out.println("read to write test");
ReadWriteLock lock = new ReentrantReadWriteLock();
lock.readLock().lock(); // get our own read lock
lock.writeLock().lock(); // upgrade to write lock
System.out.println("passed");
}
}
read to write test