Previous | Next | Trail Map | Writing Java Programs | Threads of Control


The notifyAll and wait Methods

The get and put methods in the CubbyHole object both make use of the notifyAll and wait methods to coordinate getting and putting values into the CubbyHole. Both notifyAll and wait are members of the java.lang.Object class.


Note: The notifyAll and wait methods can be invoked only by the thread that holds the lock.

Let's investigate CubbyHole's use of the notifyAll method by looking at the get method.

The notifyAll Method

The get method calls notifyAll as the last thing it does (besides return). The notifyAll method notifies all the threads waiting on the monitor held by the current thread and wakes them up. Typically, one of the waiting threads will grab the monitor and proceed.

In the case of the producer/consumer example, the Consumer thread calls the get method, so the Consumer thread holds the monitor for the CubbyHole during the execution of get. At the end of the get method, the call to notifyAll wakes up the Producer thread that is waiting to get the CubbyHole's monitor. Now, the Producer thread can get the CubbyHole monitor and proceed.

public synchronized int get() {
    while (available == false) {
        try {
            wait();
        } catch (InterruptedException e) {
        }
    }
    available = false;
    notifyAll();           // notifies Producer
    return contents;
}
If multiple threads are waiting for a monitor, the Java runtime system chooses one of the waiting threads to run, making no commitments or guarantees about which thread will be chosen.

The put method works in a similar fashion to get, waking up the Consumer thread that is waiting for the Producer to release the monitor.

The Object class has another method--notify--that arbitrarily wakes up one of the threads waiting on the monitor. In this situation, each of the remaining waiting threads continues to wait until the monitor is relinquished and it is chosen by the runtime system. The use of notify can be ill-conditioned, that is, it can fail when the conditions are changed just a little. Solutions based on notifyAll tend to be more robust. A programmer who is not going to analyze a multi-threaded program sufficiently carefully is better off using notifyAll.

The wait Method

The object wait method causes the current thread to wait (possibly forever) until another thread notifies it of a condition change. You use wait in conjunction with notify or notifyAll to coordinate the activities of multiple threads using the same resources.

The get method contains a while statement that loops until available becomes true. If available is false--the Producer has not yet produced a new number and the Consumer should wait--the get method calls wait.

The while loop contains the call to wait. The wait method waits indefinitely for a notification from the Producer thread. When the put method calls notifyAll, a Consumer wakes up from the wait state and continues within the while loop. Presumably, the Producer has generated a new number and the get method drops out of the while loop and proceeds. If the Producer has not generated a number, get goes back to the beginning of the loop and continues to wait until the Producer generates a new number and calls notifyAll.

public synchronized int get() {
    while (available == false) {
        try {
            wait();          // waits for notifyAll() call from Producer
        } catch (InterruptedException e) {
        }
    }
    available = false;
    notifyAll();
    return contents;
}

The put method works in a similar fashion, waiting for the Consumer thread to consume the current value before allowing the Producer to produce a new one.

Besides the version used in the producer/consumer example, which waits indefinitely for notification, the Object class contains two other versions of the wait method:

wait(long timeout)
Waits for notification or until the timeout period has elapsed. timeout is measured in milliseconds.
wait(long timeout, int nanos)
Waits for notification or until timeout milliseconds plus nanos nanoseconds have elapsed.


Note: Besides using these timed wait methods to synchronize threads, you can also use them in place of sleep. Both methods delay for the requested amount of time, but you can easily wake up wait with a notify. The thread can then see immediately that it should go away. This doesn't matter too much for threads that don't sleep for long, but it could be important for threads that sleep for minutes at a time.

Monitors and the notify and wait Methods

You might have noticed a potential problem in CubbyHole's put and get methods. At the beginning of the get method, if the value in the CubbyHole is not available (the Producer has not generated a new number since the last time the Consumer consumed it), then the Consumer waits for the Producer to put a new value into the CubbyHole. So, the question arises--how can the Producer put a new value into the CubbyHole if the Consumer holds the monitor? (The Consumer holds the CubbyHole's monitor because it's within the synchronized method get.)

Similarly, at the beginning of the put method, if the value in the CubbyHole has not yet been consumed, then the Producer waits for the Consumer to consume the value in the CubbyHole. And again the question arises--how can the Consumer consume the value in the CubbyHole, if the Producer holds the monitor? (The Producer holds the CubbyHole's monitor because it's within the synchronized method put.)

Well, the designers of the Java language thought of this too. When the thread enters the wait method, which happens at the beginning of both the put and get methods, the monitor is released atomically, and when the thread exits the wait method, the monitor is acquired again. This gives the waiting object the opportunity to acquire the monitor and, depending on who's waiting, consume the value in the CubbyHole or produce a new value for the CubbyHole.


Previous | Next | Trail Map | Writing Java Programs | Threads of Control