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

Programming Java threads in the real world, Part 4

Condition variables and counting semaphores -- filling in a few chinks in Java's threading model

  • Print
  • Feedback

Page 5 of 7

Listing 2 shows a simple implementation of such a beast. There's not much to it -- the vast majority of the file is comments. The main thing is the _is_true flag declared at the top of the class. The _is_true flag stores the state of the condition variable. You can set the condition to true or false by calling set_true() or set_false(). You can test the current state without blocking by calling is_true() or is_false(). You can block, waiting for the condition to become true by calling wait_for_true(), which doesn't block at all if the condition happens to be true when you call it.

Listing 2: A condition-variable implementation
001 | package com.holub.asynch;
002 | 
003 | //-------------------------------------------------------
004 | // This code (c) 1998 Allen I. Holub. All rights reserved.
005 | //-------------------------------------------------------
006 | // This code may not be distributed by yourself except in binary form,
007 | // incorporated into a Java .class file. You may use this code freely
008 | // for personal purposes, but you may not incorporate it into any
009 | // commercial product without express permission of Allen I. Holub in writing.
010 | //-------------------------------------------------------
011 | 
012 | /**
013 |  *  This class implements a simple "condition variable." The notion
014 |  *  is that a thread waits for some condition to become true.
015 |  *  If the condition is false, then no wait occurs.
016 |  *
017 |  *  Be very careful of nested-monitor-lockout here:
018 |  * <PRE>
019 |  *   class lockout
020 |  *   {  Condition godot = new Condition(false);
021 |  *   
022 |  *      synchronized void f()
023 |  *      {   
024 |  *          some_code();
025 |  *          godot.wait_for_true();
026 |  *      }
027 |  *   
028 |  *      synchronized void set() // Deadlock if another thread is in f()
029 |  *      {   godot.set_true();
030 |  *      }
031 |  *   }
032 |  * </PRE>
033 |  *  You enter f(), locking the monitor, then block waiting for the
034 |  *  condition to become true. Note that you have not released the
035 |  *  monitor for the "lockout" object. [The only way to set godot true
036 |  *  is to call set(), but you'll block on entry to set() because
037 |  *  the original caller to f() has the monitor containing "lockout"
038 |  *  object.]
039 |  *  <p>Solve the problem by releasing the monitor before waiting:
040 |  * <PRE>
041 |  *   class okay
042 |  *   {  Condition godot = new Condition(false);
043 |  *   
044 |  *      void f()
045 |  *      {   synchronized( this )
046 |  *          {   some_code();
047 |  *          }
048 |  *          godot.wait_for_true();  // Move the wait outside the monitor
049 |  *      }
050 |  *   
051 |  *      synchronized void set()
052 |  *      {   godot.set_true();
053 |  *      }
054 |  *   }
055 |  * </PRE>
056 |  * or by not synchronizing the `set()` method:
057 |  * <PRE>
058 |  *   class okay
059 |  *   {  Condition godot = new Condition(false);
060 |  *   
061 |  *      synchronized void f()
062 |  *      {   some_code();
063 |  *          godot.wait_for_true();
064 |  *      }
065 |  *   
066 |  *      void set()              // Remove the synchronized statement
067 |  *      {   godot.set_true();
068 |  *      }
069 |  *  }
070 |  * </PRE>
071 |  * The normal wait()/notify() mechanism doesn't have this problem since
072 |  * wait() releases the monitor, but you can't always use wait()/notify().
073 |  */
074 | 
075 | 
076 | public class Condition
077 | {
078 |     private boolean _is_true;
079 | 
080 |     /** Create a new condition variable in a known state.
081 |      */
082 |     public Condition( boolean is_true ){ _is_true = is_true; }
083 | 
084 |     /** See if the condition variable is true (without releasing).
085 |      */
086 |     public synchronized boolean is_true()  { return _is_true; }
087 | 
088 |     /** Set the condition to false. Waiting threads are not affected.
089 |      */
090 |     public synchronized void set_false(){ _is_true = false; }
091 | 
092 |     /** Set the condition to true. Waiting threads are not released.
093 |      */
094 |     public synchronized void set_true() { _is_true = true; notifyAll(); }
095 | 
096 |     /** Release all waiting threads without setting the condition true
097 |      */
098 |     public synchronized void release_all(){ notifyAll(); }
099 | 
100 |     /** Release one waiting thread without setting the condition true
101 |      */
102 |     public synchronized void release_one(){ notify(); }
103 | 
104 |     /** Wait for the condition to become true.
105 |      *  @param timeout Timeout in milliseconds
106 |      */
107 |     public synchronized void wait_for_true( long timeout )
108 |                                             throws InterruptedException
109 |     {   if( !_is_true )
110 |             wait( timeout );
111 |     }
112 | 
113 |     /** Wait (potentially forever) for the condition to become true.
114 |      */
115 |     public synchronized void wait_for_true() throws InterruptedException
116 |     {   if( !_is_true )
117 |             wait();
118 |     }
119 | }


Listing 3 below is basically Listing 1 rewritten to use a Condition object. Now, a call to read_line() after the user enters the text works just fine because the condition will be in the true state, and wait_for_true() won't block. Notice that read_line() has to explicitly set the condition back to false after it has read the line.

  • Print
  • Feedback

Resources
  • All the real code discussed in this article (the stuff in the com.holub.asynch package) is available in the "Goodies" section on my Web site. The version on the Web site should be considered the definitive version -- at least it corrects any bugs I know about. http://www.holub.com