Consider this JDK
standard interface:
public interface ReadWriteLock{
public Lock readLock();
public Lock writeLock();
}
B. Goetz in Java Concurrency in practice mentioned upgrading from readLock to writeLock is deadlock-prone.
If two readers simultaneously attempt to upgrade to a write lock, neither will realese the read lock.
The thing that's confused me is that it was two readers attempting to upgrade. But even one reader is enough, isn't? If a reader tries to upgrade it's not released the read lock yet. Trying to acquire the write lock with the read lock held is deadlocky.
So judging by that I think it's even theoretically nonsensical to provide the upgrade operation. Or maybe an implementation can take care of that?
It sounds like you are considering a "read lock" and a "write lock" to be two different locks which compose a read-write lock. That's not the correct way to think about it, even though the API seems to expose such a composition (by providing methods to obtain a reference to the "write lock" and to the "read lock").
(This is all confused a little by the overloading of the term "lock" - it is both a verb and a noun, that is, one can lock a lock).
Rather than thinking that ReadWriteLock
's readLock
method and writeLock
method return actual locks, consider that what they actually do is return an abstract mechanism allowing acquisition of different types of lock (on the same, single, read-write lock mechanism).
A read-write lock (mechanism) is a single lock which can be locked in two ways: read-locked and write-locked. The read-write lock can be read-locked by one or more threads concurrently, or it can be write-locked. It can never be both read- and write- locked at the same time.
If a reader tries to upgrade it's not released the read lock yet. Trying to acquire the write lock with the read lock held is deadlocky.
A write lock is stronger than a read lock; it is like a read lock with an additional property (that no other thread may also hold the lock). Upgrading from a read-lock to a write-lock is not about acquiring one lock when another is already held; rather, it's about changing the type of lock which is already held on a single lock.
So, there is no conceptual problem with a single thread upgrading its read lock to a write lock; it just needs to wait until all other holders of the read lock relinquish it before the upgrade can occur. There is no possibility of deadlock in this case.
For instance, suppose there are three threads - A, B, and C, all which have read-locked the lock. Thread A tries to upgrade to a write-lock. This means it must wait for B and C to relinquish their read lock. This eventually happens, and at that point thread A acquires a write lock (and, eventually, thread A will then relinquish this lock).
On the other hand, consider if both A and B try to upgrade to a write lock. This means they are both waiting for the other one to relinquish the read lock, which won't happen; for thread A to relinquish the read lock, it would first need to acquire the write lock, which won't happen until thread B relinquishes the read lock, which it won't do until it acquires the write lock, which won't happen until thread A relinquishes the read lock ... and so on. There is a deadlock.
The Java API doesn't allow upgrading from a read-lock to a write-lock, since disallowing it prevents the deadlock scenario. It would be possible (if a little tricky) to write a program that could safely upgrade the lock type, but the Java API doesn't allow it regardless.