Recommended: Sing it, brah! 5 fabulous songs for developers
JW's Top 5
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
Page 4 of 5
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 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.
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.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.
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.