import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * This code demonstrates a bug in fair reentrant read/write * locks under Java5. The sequence is: * * 1) Thread 1 takes read lock * 2) Thread 2 attempts a write lock (should hang) * 3) Thread 1 attempts a re-entrance * * At this point we're left hanging as the attempted write * lock blocks thread 1 from re-entering the lock. Expected * behavior is for #3 above to succeed. * * @author Lars J. Nilsson */ public class Tester { public static void main(String[] args) { try { new Tester().run(); } catch (InterruptedException e) { e.printStackTrace(); } } private void run() throws InterruptedException { // A flag used to tell the reader to proceed. AtomicBoolean flag = new AtomicBoolean(false); ReentrantReadWriteLock l = new ReentrantReadWriteLock(true); ReadLockThread r = new ReadLockThread(l.readLock(), flag); WriteLockThread w = new WriteLockThread(l.writeLock()); /* * Start the reader, this takes a read lock and waits * for 'flag' to be set to true before proceeding to * attempt a re-entrance. */ r.start(); // Wait a while to make sure 'r' has started Thread.sleep(100); /* * Start writer, it attempts a write lock which should block * until 'r' is finished and then proceed normally. */ w.start(); // Tell 'r' to attempt a re-entrance flag.set(true); // Wait for the threads to finish Thread.sleep(200); if(w.isAlive()) { /* * At this point we have a deadlock as the readers re-entrance * is blocked by the requested write lock. This is not the expected * behavior. */ System.err.println(" !!! Error: Writer failed to acquire lock"); System.exit(1); } } private static class WriteLockThread extends Thread { private final Lock lock; WriteLockThread(Lock l) { this.lock = l; } @Override public void run() { System.out.println("Writer Attempting Lock"); lock.lock(); System.out.println("Writer Locked"); lock.unlock(); System.out.println("Writer Unlocked"); } } private static class ReadLockThread extends Thread { private final Lock lock; private final AtomicBoolean proceedFlag; ReadLockThread(Lock l, AtomicBoolean proceedFlag) { this.proceedFlag = proceedFlag; this.lock = l; } @Override public void run() { lock.lock(); System.out.println("Reader Locked"); while(!proceedFlag.get()) { try { Thread.sleep(100); } catch (InterruptedException e) { } } System.out.println("Reader Attempting Lock 2"); lock.lock(); System.out.println("Reader Locked 2"); lock.unlock(); System.out.println("Reader Unlocked 2"); lock.unlock(); System.out.println("Reader Unlocked"); } } }