Wizard API updated!
Tim Boudreau has released a new version of the Swing Wizard library (version 0.997) that fixes the WizardException bug reported in JavaWorld's recent Open Source Java Project profile. The article's examples have been reworked to test out the new, improved WizardException. Thanks, Tim, for this helpful fix!
Open Source Java Projects: The Wizard API

Newsletter sign-up

Sign up for our technology specific newsletters.

Enterprise Java
View all newsletters

Email Address:

Synchronizing threads in Java, Part 1

Threads can do so much more than stream text along the bottom of your browser: synchronization is the key

The nice thing about threads in Java is that they are always there. This has hindered the porting of Java to some platforms not offering native threads (like Windows 3.1) because the person doing the port has to both port Java and create a threads package for it to use. Once you start to use threads effectively, you will not want to go back to more pedestrian single-threaded programming.

A Java programmer's first exposure to threads is usually an applet that uses them to provide animation. In these applets, the thread simply sleeps for a period of time before updating the next frame or moving text in an animated ticker. Threads, however, are much more useful than this. Another way to use threads is with the wait() and notify() functions that are part of the Object class.

Every Java object instance and class potentially has a monitor associated with it. I say potentially because if you don't use any of the synchronization functions, the monitor is never actually allocated, but it's waiting there just in case.

A monitor is simply a lock that serializes access to an object or a class. To gain access, a thread first acquires the necessary monitor, then proceeds. This happens automatically every time you enter a synchronized method. You create a synchronized method by specifying the keyword synchronized in the method's declaration.

During the execution of a synchronized method, the thread holds the monitor for that method's object, or if the method is static, it holds the monitor for that method's class. If another thread is executing the synchronized method, your thread is blocked until that thread releases the monitor (by either exiting the method or by calling wait()).

To explicitly gain access to an object's monitor, a thread calls a synchronized method within that object. To temporarily release the monitor, the thread calls the wait() function. Because the thread needs to have acquired the object's monitor, calling wait() is supported only inside a synchronized method. Using wait() in this way allows the thread to rendezvous with another thread at a particular synchronization point.

A very simple example of wait() and notify() is described in the following three classes.

The first class is named PingPong and consists of a single synchronized method and a state variable. The method is hit() and the only parameter it takes is the name of the player who will go next.

The algorithm is essentially this:

    If it is my turn,
        note whose turn it is next,
        then PING,
        and then notify anyone waiting.
    otherwise,
        wait to be notified.


To implement this, however, we add a few more lines:

 1    public class PingPong {
 2        // state variable identifying whose turn it is.
 3        private String whoseTurn = null;
 4
 5        public synchronized boolean hit(String opponent) {
 6          
 7      String x = Thread.currentThread().getName();
 8    
 9          if (whoseTurn == null) {
10          whoseTurn = x;
11              return true;
12      }
13    
14      if (x.compareTo(whoseTurn) == 0) {
15          System.out.println("PING! ("+x+")");
16          whoseTurn = opponent;
17          notifyAll();
18      } else {
19          try {
20              long t1 = System.currentTimeMillis();
21              wait(2500);
22              if ((System.currentTimeMillis() - t1) > 2500) {
23                  System.out.println("****** TIMEOUT! "+x+
24              " is waiting for "+whoseTurn+" to play.");
25              }
26          } catch (InterruptedException e) { }
27      }
28      return true; // keep playing.
29      }
30  }


In line 3 we declare our state variable, whoseTurn. This is declared private since the users of the class don't need to know it. Line 5 declares our method and it must have the synchronized keyword or the call to wait() will fail.

In line 7 we get our own name from the thread object. As you will see later, we set this after the thread is created. This helps in debugging since our thread is named something useful and is a convenient way to identify the players.

Lines 9 through 12 solve the problem of whose turn it is before anyone has gone. The policy implemented is that the first thread to invoke this method will get the honor of going first.

Lines 14 through 17 execute when it is the current thread's turn to go. When executed, the thread updates the state variable with the next thread's turn. This is done before the notify, as the notify may cause another thread to start running immediately before it knows it is its turn to run. Then notifyAll() is called to notify all threads that are waiting on this object that they can run. If you are using only two threads, simply call notify() since that call will wake up exactly one thread from the set waiting to run. With two threads, only one thread can be waiting, so the correct thread will wake up. If you extend this to three or more threads, however, the notify call may not wake up the correct thread and the system will stop until that thread's wait times out.

Lines 19 through 26 execute when it isn't the current thread's turn to go. Line 21 simply calls wait() and goes to sleep. However, you will notice that in line 20 the code notes the current time. It does this because when execution continues after the wait call returns, the reason for continuing could be either the wait timed out or our thread was awakened with a call to notify(). The only way to tell the difference is to measure how long the thread was asleep.

This timeout test is performed in line 22. If a timeout occurs, an informative message is printed to the console. In practice this will happen only when the time spent in lines 14 through 17 is greater than 2.5 seconds.

Line 26 is where we catch InterruptedException, which would be thrown if the thread in the wait() call stops prematurely.

Really, that is all there is to this part of the code. I did, however, add some additional code (shown below) between lines 8 and 9 to allow a third thread to cause the threads using this class to exit.

 8.01       if (whoseTurn.compareTo("DONE") == 0)
 8.02           return false;
 8.03
 8.04       if (opponent.compareTo("DONE") == 0) {
 8.05           whoseTurn = opponent;
 8.06           notifyAll();
 8.07           return false;
 8.08       }


As you can see, this is done by setting the special opponent DONE in the call to hit(). When the opponent is done, line 8.02 makes sure the code returns the boolean false.

1 | 2 | 3 |  Next >
Resources