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 4 of 7

Send in the cavalry
So what's one way for two threads to communicate with each other? (That's a rhetorical question.) Use a semaphore (think Napoleon, flags, mountain tops). To the rescue comes the semaphore known as a condition variable. To rehash from previous months' material: the basic notion of a condition variable is that some thread waits (is suspended) on the condition variable until the condition it represents becomes true. Every Java object has a condition variable associated with it -- in the same way it has the mutex used to guard the monitor. You wait for the condition to become true by calling wait(), and you set the condition to true by calling notify(). (The notify() call doesn't work in quite this way; I'll talk more about this in a moment.) It's easy enough to do a roll-your-own condition variable that solves the current thread-communication problem by using wait() and notify().

Listing 1 demonstrates how to do this. A condition variable called text_has_been_entered is declared up at the top of the class definition. (We're going to wait for the text-has-been-entered condition to become true.) The actionPerformed() method doesn't read the text at all; rather, it simply notifies the condition variable, setting the condition to true. Note that Java requires you to be in the monitor for an object before you can call wait() or notify() on that object, so the synchronized(text_has_been_entered) statement is mandatory. (You must be holding the lock associated with the object, by being in either a synchronized function of that object's class or a standalone synchronized statement whose argument is the object on which you're synchronizing.)

The synchronized(text_has_been_entered) statement on line 15 is mandatory, since entering the synchronized block puts us into the monitor of the object referenced by text_has_been_entered.

Meanwhile, down in read_line() on line 33, the thread that calls read_line() is waiting for the condition to become true. When this happens, the new text value is read and returned. The read_line() method is itself synchronized so that two threads can't attempt to read the same line simultaneously. It's now possible to have a simple loop like the one in main()

while( (input = source.read_line()) != null )
System.out.println("Got: " + input );


which blocks until a new input line arrives.

Listing 1: Using wait() and notify() for a condition variable


01 | import java.awt.*;
02 | import java.awt.event.*;
03 | 
04 | public class Input_source extends Frame
05 | {
06 |     int[]   text_has_been_entered = new int[1]; // The condition variable
07 | 
08 |     TextField input = new TextField();
09 | 
10 |     public Input_source()
11 |     {
12 |         input.addActionListener
13 |         (   new ActionListener()
14 |             {   public void actionPerformed( ActionEvent e )
15 |                 {   synchronized( text_has_been_entered )
16 |                     {   text_has_been_entered.notify();  // set the condition true
17 |                     }
18 |                 }
19 |             }
20 |         );
21 | 
22 |         add(input);
23 |         pack();
24 |         show();
25 |     }
26 | 
27 |     /** A blocking function that works like readLine(), but gets its
28 |      *  text from the current window's text area. The function doesn't
29 |      *  return until somebody types a line of text, whereupon it returns
30 |      *  the line. Returns null if the user types an empty line.
31 |      */
32 | 
33 |     synchronized String read_line() throws InterruptedException
34 |     {   synchronized( text_has_been_entered )
35 |         {   text_has_been_entered.wait(); // wait for the condition to become true
36 |         }
37 |         
38 |         String entered = input.getText();
39 |         input.setText("");
40 |         return (entered.length() == 0) ? null : entered;
41 | 
42 |     }
43 | 
44 | 
45 |     static public void main( String[] args ) throws Exception
46 |     {   Input_source source = new Input_source();
47 |         String input;
48 | 
49 |         while( (input = source.read_line()) != null )
50 |             System.out.println("Got: " + input );
51 | 
52 |         System.exit(0);         // kill the AWT Thread on exit
53 |     }
54 | }


But Wellington is coming
A subtle problem occurs when using Java's built-in condition variable, however. What if you call read_line() just after the value has changed rather than before? You'll just wait until the value is changed again, missing the first value entirely. The problem is that the built-in condition variable doesn't really have a notion of state associated with it. What we really need, to solve the current problem, is a true condition variable -- one that blocks only if we try to wait() when the condition is false, and that doesn't block at all if the condition is true.

  • 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