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 3 of 5

  1. findMe(), to find the element in the linked list corresponding to the current thread
  2. firstWriterIndex(), to find the very first writer
  3. lastGrantedIndex(), to find the very last element that has a granted lock


We will see the use of those three methods when we implement the forReading(), forWriting(), upgrade(), downgrade(), and release() methods. That linked list serves as the queue in which threads waiting to obtain a lock are placed. Each element in that linked list is of type WaitingListElement. Each element is created by a thread. That thread's ID is the key to its corresponding element. I have overridden the equals() method in the WaitingListElement class to define two elements as equal if their keys are equal.

forReading() and forWriting() methods delegate their no argument versions to their argument taking siblings. (That was too easy; I like that delegation stuff.)

The forReading() method

Let's look at the forReading() (the argument-taking version) method implementation. That method tries to find the element corresponding to the thread on which the call is being made. If an element is not found, forReading() creates a new one and adds it to the waiting list. If an element is found, that indicates that the thread already owns a lock. It does not matter whether the lock owned is read or write, since the write lock includes read as part of it. Therefore, the lock count increases and the method returns. If the read lock cannot be granted, the thread waits if instructed to do so, as indicated by the wait-time parameter passed into the method call. Each time the thread wakes up, either because it timed out or because it was notified to wake up, it checks the condition to see if the read lock can be granted. That is referred to as the double-checked locking pattern. (see Chapter 20, "Double-Checked Locking" by Doug Schmidt and Tim Harrison in Pattern Languages of Program Design 3 [Software Patterns Series], Robert C. Martin, Dirk Riehle, Frank Buschmann, and John Vlissides in Resources for a discussion of that pattern.) Double-checked locking prevents erroneous locking conditions. Finally, if the lock cannot be obtained due to a time-out, a false value is returned to the method caller. Before returning that false value, the method cleans up the waiting list by removing the element corresponding to the calling thread from the list. We also notify all waiting threads so that they can be granted locks if possible.

The forWriting() method

Now, let's look at the forWriting() (again, the argument-taking version) method implementation. It performs similar functions as the forReading() implementation, with a few exceptions. A thread is granted a write lock only when the element corresponding to that thread is the first in line, that is, there are no other threads with locks. In that case, when searching the waiting list for an element corresponding to that thread, the lock type matters. There are two cases to consider. If the lock type is for writing, the method simply returns with a true value. If the lock type is for reading, an attempt is made to upgrade the lock. If that attempt succeeds, the lock count increases, and the method returns a true value to the caller. That process may fail either because upgrades are not allowed by the implementation or because of a timeout. If either occurs, the method returns a false value.

  • Print
  • Feedback

Resources
  • Source code
  • Books