Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

Lock on to an alternate synchronization mechanism

Learn how to implement a reader/writer lock for flexible protection

  • Print
  • Feedback

Page 4 of 5

The upgrade() method

upgrade() is probably the trickiest method. Since a thread can only upgrade a lock if it owns one in the first place, an element corresponding to the calling thread must be in the waiting list. If an element does not exist, a LockNotHeld exception is thrown. If the thread already owns a write lock, the method returns. However, if the thread owns a read lock, we must do two things.

First, we must find the last read lock that was granted in the waiting list. Place the element for that thread behind the element corresponding to the last granted read lock that we found. That process is important because there may be other threads that were granted read locks after the calling thread and we cannot upgrade until those locks are released. Note that a write lock cannot be waiting in the queue. Because the thread making the call has a read lock, no other thread can have a write lock. All threads wanting a write lock would have to wait until all read locks are released. That is in tune with the way a reader/writer lock works.

Second, we update the type of lock to write so that new threads coming in to receive a read lock are blocked. Now we proceed in a similar way to obtaining a write lock from scratch. Finally, if the upgrade failed due to a timeout, we set the lock type back to read and notify all waiting threads so that they can be granted read or write locks if possible.

The downgrade() method

The downgrade() method is more straightforward. Like the upgrade() method, the calling thread must own a lock to call downgrade(), otherwise a LockNotHeld exception is thrown. All the waiting threads are notified when a write lock is downgraded. That allows other threads to be granted read or write locks when possible.

The release() method

Finally, let's look at the release() method. As in the upgrade() and downgrade() methods, the calling thread must own a lock to call that method; otherwise it throws a LockNotHeld exception. Each time a thread calls release(), the lock count decreases. When the lock count drops to zero, all the waiting threads are notified so that they can receive read or write locks if possible.

Test the reader/writer lock implementation

Test.java, is an example test program that exercises the implementation's functionality. Test.log provides the output produced by the test program. Both of those files can be downloaded in Resources.

I have tested the reader/writer lock on Windows NT 4.0 (SP5) on my laptop, which has a PII 400 MHz processor with 288 MB of RAM, and on Solaris 2.6 on a Sun Ultra-2, with two 400 MHz UltraSPARC-II processors and 512 MB of RAM. In both cases, I used the Sun Microsystems JVM that comes with the JDK 1.2.2 download. Testing on the Solaris box included both native and green threads.

Assuming that all the threads (1 through 7) begin in order, let's trace the program flow.

  1. The program starts off by creating thread 1 to receive a read lock.
  2. Thread 1 receives the read lock.
  3. Thread 2 wants a write lock. It blocks because thread 1 already has a read lock.
  4. Threads 3, 4, and 5 each want a read lock, but block because thread 2 is in line ahead of them, waiting for a write lock.
  5. Thread 6 wants a write lock, and thread 7 wants a read lock. Both threads block. Hence, currently all threads except thread 1 are blocked.
  6. After 2 seconds, thread 1 releases its lock, allowing thread 2 to get the write lock.
  7. All other threads still block since no other threads can obtain any lock while another thread has a write lock.
  8. After 2 more seconds, thread 2 then releases its lock.
  9. Threads 3, 4, and 5 simultaneously receive the read locks.
  10. Thread 4 wants to upgrade but has to wait because of threads 3 and 5. However, thread 4 still has priority over thread 6 for obtaining the write lock (as defined by the interface contract).
  11. After yet another 2 seconds, threads 3 and 5 release their read locks, thus allowing thread 4 to upgrade.
  12. Thread 4 then releases the write lock after 2 seconds.
  13. Now thread 6 can receive the write lock. Thread 7, which wants a read lock, still blocks.
  14. After 1 second, thread 6 downgrades its lock, and thread 7 receives a read lock.
  15. After another 2 seconds, threads 6 and 7 release their locks.
  16. Thread 2 illustrates how each forWriting() has a matching release call, and the additional release call generates a LockNotHeld exception.


The program does not test the wait time facility, which is fairly easy to verify. For example, have one thread receive a write lock to hold for 10 seconds. Have another thread try to receive a read lock now with a wait time of 2 seconds. The second thread will time out, which will be indicated by the false return value.

  • Print
  • Feedback

Resources
  • Source code
  • Books