Recipe 24.6 Simplifying Synchronization with 1.5 Locks


Problem

You want an easier means of synchronizing threads.

Solution

Use the Lock mechanism, new in 1.5.

Discussion

JDK 1.5 introduced the java.util.concurrent.locks package; its major interface is Lock. This interface has several methods for locking and one for unlocking. The general pattern for using it is:

Lock thelock = .... try  {         lock.lock( );         // do the work that is protected by the lock } finally {         lock.unlock( ); }

The point of putting the unlock( ) call in the finally block is, of course, to ensure that it is not bypassed if an exception occurs (the code may also include one or more catch blocks, as required by the work being performed).

The improvement here, compared with the traditional synchronized methods and blocks, is that using a Lock actually looks like a locking operation! And, as I mentioned, several means of locking are available, shown in Table 24-1.

Table 24-1. Locking methods of the Lock class

Return type

Method

Meaning

void

lock( )

Get the lock, even if you have to wait until another thread frees it first.

boolean

tryLock( )

Get the lock only if it is free right now.

boolean

tryLock(long time, TimeUnit units) throws InterruptedException

Try to get the lock, but only wait for the length of time indicated.

void

lockInterruptibly( ) throws InterruptedException

Get the lock, waiting unless interrupted.

void

unlock( )

Release the lock.


The TimeUnit class lets you specify the units for the amount of time specified, including: TimeUnit.SECONDS, TimeUnit.MILLISECONDS, TimeUnit.MICROSECONDS, and TimeUnit.NANOSECONDS.

In all cases the lock must be released with unlock( ) before it can be locked again.

The standard Lock is useful in many applications, but depending on the application's requirements, other types of locks may be more appropriate. Applications with asymmetric load patterns may benefit from a common pattern called the "reader-writer lock"; I call this one a Readers-Writer lock to emphasize that there can be many readers but only one writer. It's actually a pair of interconnected locks; any number of readers can hold the read lock and read the data, as long as it's not being written (shared read access). A thread trying to lock the write lock, however, waits until all the readers are finished, then locks them out until the writer is finished (exclusive write access). To support this pattern, JDK 1.5 provides the ReadWriteLock interface and the implementing class ReentrantReadWriteLock. The interface has only two methods, readLock( ) and writeLock( ), which provide a reference to the appropriate Lock implementation. These methods do not, in themselves, lock or unlock the locks; they only provide access to them, so it is common to see code like:

rwlock.readLock( ).lock( ); ... rwlock.readLock( ).unlock( );

To demonstrate ReadWriteLock in action, I wrote the business logic portion of a web-based voting application. It could be used in voting for candidates or for the more common web poll. Presuming that you display the results on the home page and change the data only when somebody takes the time to click on a response to vote, this application fits one of the intended criteria for ReadWriteLock i.e., that you have more readers than writers. The main class, ReadersWritersDemo, is shown in Example 24-10. The helper class BallotBox is online; it simply keeps track of the votes and returns an invariant Iterator upon request. Note that in the run( ) method of the reading threads, I obtain the iterator while holding the lock but release the lock before printing it; this allows greater concurrency and better performance.

Example 24-10. ReadersWriterDemo.java
import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.concurrent.locks.ReentrantReadWriteLock; /**  * Simulate multiple readers   */ public class ReadersWriterDemo {         private static final int NUM_READER_THREADS = 3;         public static void main(String[] args) {                 new ReadersWriterDemo( ).demo( );         }         /** Set this to true to end the program */         private boolean done = false;         /** The data being protected. */         private BallotBox theData;         /** The read lock / write lock combination */         private ReentrantReadWriteLock lock = new ReentrantReadWriteLock( );                  /**          * Constructor: set up some quasi-random initial data          */         public ReadersWriterDemo( ) {              List questionsList = new ArrayList( );              questionsList.add("Agree");              questionsList.add("Disagree");              theData = new BallotBox(questionsList);         }                  /**          * Run a demo with more readers than writers          */         private void demo( ) {                                  // Start two reader threads                 for (int i = 0; i < NUM_READER_THREADS; i++) {                     new Thread( ) {                         public void run( ) {                             while(!done) {                                 Iterator results = null;                                 try {                                     lock.readLock( ).lock( );                                     results = theData.iterator( );                                 } finally {                                      // Unlock in finally to be sure.                                      lock.readLock( ).unlock( );                                 }                                 // Now lock has been freed, take time to print                                 print(results);                                 try {                                     Thread.sleep(((long)(Math.random( )* 1000)));                                 } catch (InterruptedException ex) {                                     // nothing to do                                 }                              }                           }                       }.start( );                 }                 // Start one writer thread to simulate occasional voting                 new Thread( ) {                      public void run( ) {                          while(!done) {                              try {                                  lock.writeLock( ).lock( );                                  theData.voteFor(                                             (((int)(Math.random( )*                                             theData.getCandidateCount( )))));                               } finally {                                    lock.writeLock( ).unlock( );                               }                               try {                                   Thread.sleep(((long)(Math.random( )*1500)));                               } catch (InterruptedException ex) {                                   // nothing to do                               }                           }                      }                 }.start( );                                  // In the main thread, wait a while then terminate the run.                 try {                     Thread.sleep(10 *1000);                 } catch (InterruptedException ex) {                     // nothing to do                 } finally {                      done = true;                 }                 }         /** print the current totals */         private void print(Iterator iter) {             boolean first = true;             while (iter.hasNext( )) {                  BallotPosition pair = (BallotPosition) iter.next( );                  if (!first)                      System.out.print(", ");                  System.out.print(pair.getName( ) + "(" + pair.getVotes( ) + ")");                  first = false;             }             System.out.println( );        }          }

Since this is a simulation and the voting is random, it does not always come out 50-50. In two consecutive runs, the following were the last line of each run:

Agree(6), Disagree(6) Agree(9), Disagree(4)

See Also

The Lock interface also makes available Condition objects, which provide even more flexibility. Consult the documentation for the 1.5 release for more information.



Java Cookbook
Java Cookbook, Second Edition
ISBN: 0596007019
EAN: 2147483647
Year: 2003
Pages: 409
Authors: Ian F Darwin

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net