Interrupted
This means that another thread invoked the interrupt() method on the waiting thread. The awakened thread is enabled as previously explained, but the return from the wait() call will result in an InterruptedException if and when the awakened thread finally gets a chance to run. The code invoking the wait() method must be prepared to handle this checked exception (p. 1393).
Spurious Wakeup
In very rare cases, a thread in a waiting state is awakened by what is called a spurious wakeup, which is not due to familiar events like being notified, timed out, or interrupted, but due to deep-level system signals warranting a wakeup call to the waiting threads. The condition on which the thread was waiting might not have been fulfilled when the thread wakes up from a spurious wakeup, and should therefore be rechecked when the thread gets to run. This is done by testing the condition in a loop that contains the wait() call, as the thread can go right back to waiting if the condition is not satisfied.
while (conditionNotSatisfied()) {
…
wait();
…
}
// Proceed …
Using Wait and Notify
The idiom is to call the wait() method in a try-catch block inside a loop, guarded by a condition that the thread expects to be set by notification.
In Example 22.6, the main() method of the class WaitNotifyScenario creates and starts four daemon threads that are manipulating the same MessageDisplay object. Two of them are continuously trying to set a message in the message display, while the other two are continuously trying to print whatever message is in the display.
Since the threads manipulate the same MessageDisplay object, and the setMessage() and displayMessage() methods in the class MessageDisplay are synchronized, it means that the threads synchronize on the same MessageDisplay object. In other words, the mutual exclusion of these operations is guaranteed on the same MessageDisplay object.
Example 22.6 Waiting and Notifying
class MessageDisplay {
private String message;
public synchronized void displayMessage() {
String threadName = Thread.currentThread().getName();
while (this.message == null) { // No message?
try {
wait(); // (1)
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(threadName + “: ” + this.message); // Display message.
this.message = null; // Remove message.
notifyAll(); // (2)
}
public synchronized void setMessage(String message) {
String threadName = Thread.currentThread().getName();
while (this.message != null) { // Message present?
try {
wait(); // (3)
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.message = message; // Set new message.
System.out.println(threadName + “: Message set is ” + this.message);
notifyAll(); // (4)
}
}
public class WaitNotifyScenario {
public static void main(String[] args) throws InterruptedException {
MessageDisplay md = new MessageDisplay();
Thread t1 = new Thread(() -> { while (true) md.setMessage(“Hi!”); }, “t1”);
t1.setDaemon(true);
t1.start();
Thread t2 = new Thread(() -> { while (true) md.setMessage(“Howdy!”); }, “t2”);
t2.setDaemon(true);
t2.start();
Thread t3 = new Thread(() -> { while (true) md.displayMessage(); }, “t3”);
t3.setDaemon(true);
t3.start();
Thread t4 = new Thread(() -> { while (true) md.displayMessage(); }, “t4”);
t4.setDaemon(true);
t4.start();
Thread.sleep(5);
System.out.println(“Exit from main.”);
}
}
Probable output from the program (edited to fit on the page):
t0: t1: Message set is Hi!
t3: Hi!
…
t2: Message set is Howdy!
t3: Howdy!
t1: Message set is Hi!
t3: Hi!
t2: Message set is Howdy!
t4: Howdy!
…
Exit from main.
t1: Message set is Hi!
t3: Hi!
t2: Message set is Howdy!
t4: Howdy!
t1: Message set is Hi!
Example 22.6 illustrates how threads waiting as a result of calling the wait() method on an object are notified by another thread calling the notifyAll() method on the same object, in order for a waiting thread to start running again.
One usage of the wait() call is shown in Example 22.6 at (1) in the synchronized displayMessage() method. When a thread executing this method on the Message-Display object finds that there is no message (this.message == null), it invokes the wait() method in order to wait for a thread to set a message.
Another use of the wait() call is shown at (3) in the synchronized setMessage() method. When a thread executing this method on the MessageDisplay object finds that there is already a message (this.message != null), it invokes the wait() method to wait for a thread to display and remove the message.
When a thread executing the synchronized method setMessage() on the Message-Display object successfully sets a message, it calls the notifyAll() method at (4). The wait set of the MessageDisplay object may contain any waiting threads that have earlier called the wait() method at either (1) or (3) on this MessageDisplay object. A single thread from the wait set is enabled for running. If this thread was executing a displayMessage() operation, it now has a chance of being successful because a message is available for display at the moment. If this thread was executing a setMessage() operation, it can try again to see if the previous message has been removed so that a new message can be set.
When a thread executing the synchronized method displayMessage() on the Message-Display object successfully prints and removes the message, it calls the notifyAll() method at (2). Again assuming that the wait set of the MessageDisplay object is not empty, one thread from the set is arbitrarily chosen and enabled. If the notified thread was executing a setMessage() operation, it now has a chance of succeeding because there is no message in the MessageDisplay object at the moment.
Note that the waiting condition at (1) for the displayMessage() operation is executed in a loop. A waiting thread that has been notified is not guaranteed to run right away. Before it gets to run, another thread may synchronize on the MessageDisplay object and remove the message. If the notified thread was waiting to display the message, it would now incorrectly try to display a nonexisting message because the condition was not tested after notification. The loop ensures that the condition is always tested after notification, sending the thread back to the waiting state if the condition is not met. To avert the analogous danger of setting a message if one already exits in the MessageDisplay object, the waiting condition at (3) for the setMessage() operation is also executed in a loop.
The output from Example 22.6 shows the behavior of the threads that set and print the message in the shared MessageDisplay object, respectively. The two operations of setting the message and displaying/removing the message are performed in lockstep.
The four threads created are daemon threads. They will be terminated if they have not completed when the main thread dies, thereby stopping the execution of the program.