Programming Java threads in the real world, Part 4

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

This month's column adds a few more classes to the threading arsenal we started building in last month's Java Toolbox column. This time I'll discuss the following:

  1. Roll-your-own versions of the condition variable, which replaces wait() and notify() in some situations

  2. Djikstra's "counting" semaphore, which is used to manage pools of resources

(A semaphore is any of several mechanisms used to synchronize and communicate between threads. Think of the "semaphore" as in the flags that two boy scouts use to talk to each other from a distance -- different flag positions represent different letters of the alphabet. Napoleon's army used the vanes of windmills on mountain tops to send semaphore messages great distances very quickly. The mutex discussed last month, since it's a communications mechanism, is also a "semaphore.")

The condition variable can often be simulated using Java alone -- and I'll show you how -- while the counting semaphore can't.

Before I start, though, I'd like to go over a few loose ends from last month and fix a few bugs.

Oops! Could my code have bugs in it?

Before leaping into this month's meat (now there's a metaphor you can sink your teeth into), let's look at a few loose ends that either caught my eye (or were brought to my attention) after last month's article went live.

During one of my book's "peer reviews," an academic reviewer once took exception to the sentence "if you're anything like me, you'll forget to ... so you should write your code to do it automatically." His comment to me was: "I would never admit that in print." This guy was (and as far as I know, still is) a tenured professor at an Ivy League university, and I suppose his comment was correct in a literal sense: since he never had written any actual code, he never had any bugs to admit. I might as well say it up front, though: my code contains an occasional bug (gasp). Consequently, I expect an "Oops" section or its moral equivalent to become a regular feature of this column. There's nothing like having 100,000 people look over your code for problems to emerge and be highlighted.

Joe Bowbeer pointed out (quite correctly):

Why not advise the use of try/finally to prevent an exception from gunking up the works?

mutex.acquire();
try
{   doit();
}
finally
{   mutex.release();
}

I prefer the [above] form of try/finally because it separates the exceptions that might occur in changing the state (acquire) from the exceptions that might occur when working in the new state (doit).

The other (more intuitive?) form is

try
{   mutex.acquire();
    doit();
}
finally
{   mutex.release();
}

This requires more programming in release() to ensure that mutex is in a consistent state: if release() is called after an exception in acquire(), the mutex may not have been acquired, or [may have been] half-acquired, etc.

I should add that Joe's last point is important in the case of last month's Mutex class. The acquire_without_blocking() method, where the actual acquisition occurs, doesn't throw any exceptions at awkward times. The only exception that can be thrown from acquire(), in fact, is an InterruptedException, thrown if a timeout is specified and the waiting thread is interrupted. This operation does not leave the mutex in an unstable state, however.

Be that as it may, while looking at my code to make sure Joe hadn't found a bug, I found a bug myself. (I'll show you the code in a moment, but let's discuss the problems in it first.) The acquire() method was using a standard spin lock while waiting to acquire the mutex, but this strategy is appropriate only when the timeout is infinitely large. I've fixed the problem by making the loop terminate for timeout values less than Integer.MAX_VALUE (the value I use for "forever"). It continues to use a spin lock in "forever" cases.

While I was at it, I also decided to have acquire() indicate whether or not it had timed out. The choices here are the usual ones: a return value or an exception toss. I opted for the latter because a timeout typically is an error condition, and I didn't want to clutter up the code with unnecessary tests for false return values.

I modified the definition for the Semaphore interface to incorporate this new exception:

01 | package com.holub.asynch;
02 | 
03 | interface Semaphore
04 | {
05 |     int  id     ();
06 |     void acquire(long timeout)  throws InterruptedException,
07 |                                        Timed_out;
08 |     void release();
09 | 
10 |     public static class Timed_out extends java.lang.RuntimeException
11 |     {   Timed_out(){ super("Timed out while waiting to acquire semaphore"); };
12 |     }
13 | }

Note that Semaphore.Timed_out is a RuntimeException, so you don't have to catch it if the timeout is a fatal error (often the case).

The new (and this time, I hope, correct) version of acquire() now looks like this:

01 | public synchronized void acquire( long timeout ) throws InterruptedException
02 | {   
03 |     if( timeout == 0 )                      // don't wait at all
04 |     {   acquire_without_blocking();
05 |     }
06 |     else if( timeout == Long.MAX_VALUE )    // wait forever
07 |     {   while( !acquire_without_blocking() ) 
08 |             this.wait( timeout );
09 |     }
10 |     else                                    // wait limited by timeout
11 |     {   if( !acquire_without_blocking() )
12 |         {   this.wait( timeout );
13 |             if( !acquire_without_blocking() )
14 |                 throw new Semaphore.Timed_out();
15 |         }
16 |     }
17 | }

Finally, in last month's column, I inadvertently used an outdated version of the Lock_manager's comparator class. (It threw a Not_Comparable exception -- an artifact of my own sort implementation, which I abandoned when Java added an official one.) Anyway, the comparator class should look like this:

private static class Lock_comparator implements Comparator
{   public int compare(Object a, Object b)
    {   return ((Semaphore)a).id() - ((Semaphore)b).id();
    }
    public boolean equals(Object obj)
    {   return obj instanceof Lock_comparator;
    }
}

I've modified the code in the "Goodies" section of my Web site (see Resources) to incorporate all these changes.

Now, on to the meat of this month's article.

Condition variables

Forging ahead -- to boldly split infinitives no one has split before:

I've brought up "condition variables" before in the context of wait() and notify(). The central concept is that a thread will wait until some condition becomes true. For example, a thread may need to wait for somebody to push a button before proceeding with an action, or a thread may wait for something to appear in an empty queue (for the queue-not-empty condition to become true).

Using a condition variable to wait for events

The following code illustrates a classic problem that is easily solved with a condition variable: How do I wait for an event to occur without wasting machine cycles in a polling loop? The code sets up a simple TextField and an ActionListener that's notified when the user types the Enter key:

01 | class Some_class extends Frame
02 | {
03 |     TextField input = new TextField();
04 |     String    entered = "";
05 | 
06 |     public The_wrong_way()
07 |     {   input.addActionListener
08 |         (   new ActionListener()
09 |             {   public void actionPerformed( ActionEvent e )
10 |                 {   entered = input.getText();
11 |                 }
12 |             }
13 |         );
14 | 
15 |         add(input);
16 |         pack();
17 |         show();
18 |     }
19 | 
20 |     String read_line(){ return entered; }
21 |     //...
22 | }

So far, so good, but let's look at the situation in more detail.

When you display the Frame, AWT fires up a thread that monitors events coming in from the operating system, including key-press events. When the Enter key is pressed, for example, the AWT thread gets the key-press event from the OS and, in turn, calls the listener's actionPerformed() method. The "actionPerformed()" messages are coming in asynchronously from the AWT event-loop thread. Put another way: the "actionPerformed()" message is actually running on that AWT thread.

Meanwhile, a user thread (as differentiated from the AWT thread) calls read_line() to find out what the user has typed. The problem is that both the AWT and the user thread can access the entered field simultaneously -- a classic race condition. The second thread could call read_line() while the AWT thread is in the middle of ActionPerformed() and end up reading garbage.

Solve this first problem with synchronization:

01 | class Some_class extends Frame
02 | {
03 |     TextField input = new TextField();
04 |     String    entered = "";
05 | 
06 |     public The_wrong_way()
07 |     {   input.addActionListener
08 |         (   new ActionListener()
09 |             {   public void actionPerformed( ActionEvent e )
10 |                 {   synchronized( Some_class.this )
11 |                     {    entered = input.getText();
12 |                     }
13 |                 }
14 |             }
15 |         );
16 | 
17 |         add(input);
18 |         pack();
19 |         show();
20 |     }
21 | 
22 |     String synchronized read_line(){ return entered; }
23 |     //...
24 | }

Note that the inner-class method has to synchronize explicitly on the outer-class object. Simply synchronizing actionPerformed() doesn't work because you'll be synchronizing on the monitor of the anonymous inner-class object, and the field you want to guard is in the outer-class object.

Moving on, our user thread needs to know when an entire line has been typed to be sure that read_line() will return a complete line of input, but (and this is the big but), there's no direct communication between the two threads involved in this transaction. The code running on the AWT thread (actionPerformed()) doesn't tell the user thread that an entire-line-has-been-typed event has occurred.

So how does the caller of read_line() know the string has changed? It could sit in a tight polling loop calling read_line() and checking the current return value against the previously returned value, but that's an awful lot of machine cycles wasted on doing nothing useful.

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.

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.

There are still a few problems that have to be fixed to make this all work in the real world. For example, there's no way for a program to know if the user overtypes a string when nobody fetches the original string before it's overwritten. Input strings should be queued up as they come in and read_line() should return a string from the queue if there is one, blocking only if the queue is empty. Listing 3 serves to illustrate the problem at hand without addressing these other issues.

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

Counting semaphores

The other semaphore I want to look at this month is the "Djikstra" counting semaphore. This one has no direct analog in Java, so it's among the more useful of the com.holub.asynch classes.

Counting semaphores are used to keep track of the availability of a resource within a pool of limited size. For example, you might have four connections open to a database server that are simply recycled to perform multiple queries. This way, you won't incur the overhead of opening a connection every time you make a query. Threads seeking to make queries should block (should be suspended, waiting), if no connections are available. They should be reactivated (released from wait) when a connection becomes available. A counting semaphore can solve this problem (though other solutions -- such as a thread-safe stack with a pop method that blocks if the stack is empty -- are also possible).

Counting semaphores are initialized with a count -- typically the number of objects available in the pool. Every time you acquire the semaphore, the count is decremented; every time you release the semaphore, it's incremented. On acquisition, if the count (after the decrement) is non-0, nothing happens, and you get your slot in the pool. If the count is 0, however, the acquiring thread blocks until some other thread releases the semaphore, thereby incrementing the count.

Counting semaphores typically have maximum counts as well as initial counts. A semaphore initialized with a count of 0, but with a maximum of 10, is effectively saying that 10 objects can be in the pool, but none of them are available right now. A reverse-sensing semaphore (which I haven't implemented) is also occasionally useful. This one blocks unless the count is 0. It's useful if you need to acquire the entire pool before you can do anything useful, or if you need to do something when the pool becomes empty (such as add extra elements).

Listing 4, below, shows my Counting_semaphore implementation. It implements the Semaphore interface introduced last month, so slots in multiple pools can be acquired safely by using last month's lock-manager class. As was the case with the Condition class, the counting semaphore is built around Java's wait() and notify() methods. The acquire() method waits if enough slots aren't available; the release() method notifies any waiting threads when a slot becomes available. If multiple threads are waiting, they'll have to sort out amongst themselves which actually gets the slot -- all but one will go back to waiting, but you can't reliably predict which one will get the slot.

I've taken the coward's way out and haven't implemented a version of acquire() that lets you get multiple slots at once. You have to acquire slots one at a time. The problem is that the number of slots you need may become available one at a time, but they may also be grabbed by other threads before the total number you need becomes available. Being forced to acquire slots one at a time actually increases your odds of getting the total number of slots you need over the odds you'd get by waiting until the semaphore's internal count came up to the total. You'll be able to suck the slots up one at a time as they become available. I do have a version of release() that lets you free up multiple slots all at once, however.

Note that the "honor system" is in use in Figure 4. An individual slot doesn't have the concept of ownership associated with it. A thread is on its honor to free up only the slots it has previously acquired. A Counting_semaphore.Too_many_releases object will be thrown if a thread tries to bring the total available-slot count above the maximum, but a thread could still incorrectly release the wrong number of slots without triggering the exception toss.

Listing 4: A counting semaphore
001 | //-------------------------------------------------------
002 | // This code (c) 1998 Allen I. Holub. All rights reserved.
003 | //-------------------------------------------------------
004 | // This code may not be distributed by yourself except in binary form,
005 | // incorporated into a java .class file. You may use this code freely
006 | // for personal purposes, but you may not incorporate it into any
007 | // commercial product without express permission of Allen I. Holub in writing.
008 | //-------------------------------------------------------
009 | 
010 | package com.holub.asynch;
011 | 
012 | import com.holub.tools.Comparable;
013 | import com.holub.asynch.Semaphore;
014 | import com.holub.asynch.Lock_manager;
015 | 
016 | class Counting_semaphore implements Semaphore
017 | {
018 |     private int         count;
019 |     private final int   max_count;
020 |   
021 |     /*****************************************************************
022 |      *  Create a counting semaphore with the specified initial and
023 |      *  maximum counts. release(), which increments the count, is not
024 |      *  permitted to increment it past the maximum. If the initial_count
025 |      *  is larger than the max_count, it is silently truncated.
026 |      *
027 |      *  @see release
028 |      */
029 |     Counting_semaphore( int initial_count, int max_count )
030 |     {
031 |         this.max_count  = max_count;
032 |         this.count      = (initial_count > max_count)
033 |                                     ? max_count : initial_count ;
034 |     }
035 | 
036 |     /*****************************************************************
037 |      *  Create a counting semaphore with a maximum count of
038 |      *  Integer.MAX_VALUE
039 |      */
040 |     Counting_semaphore( int initial_count )
041 |     {   this( initial_count, Integer.MAX_VALUE );
042 |     }
043 | 
044 |     /*****************************************************************
045 |      *  Required override of Semaphore.id(). Don't call this function.
046 |      *  @see Lock_manager
047 |      */
048 | 
049 |     public        int  id() { return _id; }
050 |     private final int  _id = Lock_manager.new_id();
051 | 
052 |     /*****************************************************************
053 |      *  Acquire the semaphore, decrementing the count. Block if the
054 |      *  count goes to zero. Bug: It's possible in some situations
055 |      *  for the timeout to be exceeded.
056 |      *
057 |      *  <p>I have deliberately not implemented a variant that allows
058 |      *  acquisition of multiple slots in a single call because it's not
059 |      *  clear what you'd do if all requested slots aren't available.
060 |      *
061 |      *  @throws InterruptedException if interrupted while waiting
062 |      *          for the semaphore.
063 |      *  @return true if we got the slot.
064 |      */
065 |     public synchronized void acquire(long timeout)
066 |                                             throws InterruptedException
067 |     {   while( count <= 0 )
068 |             this.wait( timeout );
069 |         --count;
070 |     }
071 | 
072 |     /*****************************************************************
073 |      *  Release the semaphore and increment the count.
074 |      *  This one is the generic release required by the Semaphore
075 |      *  interface, so all it can do is throw an exception if
076 |      *  there's an error.
077 |      *  @throws Counting_semaphore.TooManyReleases (a RuntimeException)
078 |      *      if you try to release a semaphore whose count is already
079 |      *      at the maximum value.
080 |      */
081 |     public synchronized void release(){ release(1); }
082 | 
083 |     /*****************************************************************
084 |      *  Release "increment" slots in the semaphore all at once.
085 |      *  @param increment The amount to increment the count.
086 |      *      If this value is zero, the current count is returned and
087 |      *      no threads are released.
088 |      *  @throws Counting_semaphore.TooManyReleases (a RuntimeException)
089 |      *      if the current value + count is greater than the maximum.
090 |      *      The semaphore will not have been modified in this case.
091 |      *  @return the value of the count after the increment is added.
092 |      */
093 |     public synchronized int release( int increment )
094 |     {
095 |         int current_count = count;
096 |         int new_count     = count + increment;
097 | 
098 |         if( new_count > max_count )
099 |             throw new TooManyReleases();
100 | 
101 |         count = new_count;
102 |         if( current_count == 0 && count > 0 )
103 |             notifyAll();
104 | 
105 |         return count;
106 |     }
107 | 
108 |     /** Thrown if you try to release more than the maximum number
109 |      * of slots.
110 |      */
111 |     public static class TooManyReleases extends RuntimeException    
112 |     {   private TooManyReleases()
113 |         {   super("Released semaphore that was at capacity");
114 |         }
115 |     }
116 | 
117 |     /*****************************************************************
118 |      *  A semaphore-specific release function, returns an error status
119 |      *  if the count would go past the initially specified maximum.
120 |      *  @returns true if the semaphore was successfully released, false
121 |      *  otherwise.
122 |      */
123 |     public synchronized boolean notifying_release()
124 |     {
125 |         if( count >= max_count )
126 |             return false;
127 | 
128 |         if( ++count == 1 )
129 |             notifyAll();
130 |         return true;
131 |     }
132 | }   

Listing 4 is pretty useful as it stands, but there's one variations on the counting-semaphore theme that deserves mention. I sometimes use what I call a "reverse sensing" semaphore. This one blocks until the pool is empty. In the pooled-database-connection example I discussed earlier, a thread that created new database connections might wait on this reverse-sensing semaphore, waking up when it needed to open additional connections. I'm sure you can think of other variations as well.

Wrapping up

So that's it for this month. We've seen how to use the built-in condition variable to communicate between threads generally (and the AWT event thread in particular). We've also seen that, though you can do a lot of what a condition variable does just by using wait() and notify(), you can't do everything. The Condition class adds the essential ability to not wait when the condition is already true. The Counting_semaphore isn't implemented as a Java primitive at all, so it can be particularly useful when you really need it to manage pooled resources.

Next month's column will continue on the current theme, presenting a Timer class that makes it easy to fire events on a regular schedule. The Timer implementation also demonstrates how to write code to suspend, resume, and stop threads without using the (now deprecated) suspend(), resume(), and stop() methods. :END_BODY

Allen Holub has been working in the computer industry since 1979. He is widely published in magazines (Dr. Dobb's Journal, Programmers Journal, Byte, MSJ, among others). He has seven books to his credit, and is currently working on an eighth that will present the complete sources for a Java compiler written in Java. Allen abandoned C++ for Java in early 1996 and now looks at C++ as a bad dream, the memory of which is mercifully fading. He's been teaching programming (first C, then C++ and MFC, now OO-Design and Java) both on his own and for the University of California Berkeley Extension since 1982. Allen offers both public classes and in-house training in Java and object-oriented design topics. He also does object-oriented design consulting. Get more information and contact Allen via his Web site http://www.holub.com.

Learn more about this topic

  • 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
Join the discussion
Be the first to comment on this article. Our Commenting Policies