Blocked thread with Read Write Lock due to writer starvation
Here is an interesting and simple example of how you can end up with a locked thread in your application and what you can do to avoid it.
Task A – Starts at t = 0. Takes a read lock. Runs for 3 seconds. Scheduled to run every 5 seconds.
Task B – Starts at t = 2. Takes a read lock. Runs for 3 seconds. Scheduled to run every 5 seconds.
t ->
AAAXXAAAXXAAAXXAAAXXAAAXX...
XXBBBXXBBBXXBBBXXBBBXXBBB...
Task C – Starts at t = 10 (or rather tries to start at any time after t = 0) . Tries to take a write lock. Can never get it!
With the scheduling above, either A or B always have the read lock. A write lock can be acquired only if there are no read locks given out. So if Task C starts later and tries to get the write lock (which it gets only when no read locks have been granted) then Task C will get stuck for eternity.
One may think that this situation is rare. But it is not. With the common scenario of many readers and few or rare writers, it can happen quite often. While the lock throughput may be high, there are no guarantees about fairness.
To counter this situation, we introduce the concept of a fairness policy in granting locks. Depending on fairness, a thread requesting a read lock may block if a writer thread is already waiting for a write lock. This means that a read lock will not be granted even if no write lock has currently been granted! This is known as a write biased read write lock. The pitfall is that the fairness overhead can reduce throughput drastically for a highly concurrent application.
The recommendation is to use truly concurrent data structures like ConcurrentHashMap or ConcurrentLinkedQueue if possible rather than going for the cheap (in terms of development time) protection of your data structures via a ReadWriteLock. If it is a custom data structure, the RWL can certainly be an easy way of increasing concurrency rather than redesigning your stuff for concurrency with volatiles, reference variables, lock striping, atomic variable, CAS (compare-and swap), condition queues and other concurrent data structure design techniques.
In Java, the class ReentrantReadWriteLock class which implements the ReadWriteLock interface takes a boolean argument in a constructor to indicate fairness.
In .NET, the ReaderWriterLockSlim class provides similar functionality.
See this article from the Java Specialists newsletter for benchmarks and numbers comparing fair and non-fair locks and their behaviour in Java 5 and Java 6.
Related posts:
- Java Synchronization Benchmark
- Assigning a reference to itself in Java has no effect, right? – WRONG!
- There is a difference between free space and usable space!
- Sending a SIGBREAK signal to a Java process to do a thread dump in Windows
Tags: Concurrency, Java, Lock, Threads
