Mudra Services
20749 Bridalveil Falls Terrace
Potomac Falls, VA 20165
(571) 243-5888   |   FAX : 1 (866) 8672363
Email :
Product Development

Useful Java Links

Pitfalls Of Wait/Notify
Author - Viraj Shetty

Introduction

One of the useful features in java is the built in support for writing multi-threaded applications. A thread, sometimes referred to as a lightweight process, is an execution path in the program which has it's own local variables, program counter and lifetime. When programs spawn threads to accomplish some task, a mechanism needs to be available for synchronizing activities among threads and to control them. Java's inbuilt support provides two important methods to do this - wait() and notify(). In this article we will present a few pitfalls in the usage of wait/notify and present the right idiom to use them.

Wait/Notify

The wait/notify mechanism allows one thread to wait for a signal from another thread to proceed with some activity. Imagine there is a synchronization object defined as a member variable :

							
private Object syncLock = new Object();

If a thread executes the block below, then the thread will wait till another thread calls the notify() method on the same synchronization object (syncLock) or the wait method is interrupted (either by a interrupt call to the thread or some other way). If the thread is interrupted, then the wait() method will throw an interrupted exception
synchronized(syncLock) { try { syncLock.wait(); } catch (InterruptedException iExp) { // Interrupt signal was sent to this Thread } }

It is essential that a lock on the target object be obtained before calling the wait() method (done above using the synchronized block). An important thing to note is that by invoking the wait() method on an object, the lock on that object is released and the thread goes to sleep until the thread is interrupted or the object is notified by notify() method.

A thread waiting on an object, can be notified to proceed by using the notify method as below. This is done from another thread. The notify method signals to one of the waiting threads to wake up. To signal all the waiting threads, use the notifyAll() method.

							
synchronized(syncLock) { syncLock.notify(); }

Example and Usage

On the face of it, two methods seem simple to use. So, let's go ahead and write some code to validate the two methods. As an Example, let us write a thread called EventThread. The sole purpose of this thread is to capture all events occurring in the system and to print a console message when an event is detected. Other threads in the system can use the EventThread to tell the system that a particular event occurred. So, with these assumptions, our example will have three classes.

Event
Represents an event in the system. This is a simple java object, which contains the event identifier and the user. In our example, the MainClass thread (since the MainClass has the main method, it runs on the main thread) will inform the EventThread of any events happening in the system by passing an instance of this class.

EventThread
Represents the thread, which handles all events. This is the event thread which will call the wait method until the MainClass thread invokes the notify method to proceed with event processing

MainClass
Represents the user of the EventThread. This is the simple controlling class, which generates all the events. This class will call the notify method to synchronize with the EventThread thread to log the event.

The first cut of the solution is presented below.

Event.java
							
package com.threads; // Represents an Event in the System public class Event { private int ID; private String user; public Event(int ID,String user) { this.ID = ID; this.user = user; } public int getID() { return ID; } public String getUser() { return user; } public void setID(int ID) { this.ID = ID; } public void setUser(String user) { this.user = user; } }

EventThread.java
							
package com.threads; import java.util.LinkedList; import java.util.List; public class EventThread extends Thread { // list of events private List eventList = new LinkedList(); public void notifyEvent(Event event) { synchronized (eventList) { eventList.add(event); eventList.notify(); } } public void run() { boolean stop = false; while(!stop) { try { Event event = null; synchronized(eventList) { eventList.wait(); event = (Event)eventList.remove(0); } // Handle the event .. System.out.println("Event processed ID : " + event.getID()); } catch (InterruptedException iExp) { stop = true; } } } }

EventThread class extends from Thread class. When the EventThread is started (from MainClass.java), the first thing the thread does is to wait for any events in the event list. All events are stored in the linked list called 'eventList'. When the MainClass, invokes the notifyEvent method of this class, the thread will wake up, remove the first element in the list and process it. It will then go back and wait for the next event. Also, this thread will terminate if the thread is interrupted using the interrupt() method. In this class, the InterruptedException exception is caught and the stop variable is set to true. In the next iteration of the while, the thread terminates. Note that the act of handling the event (printing the event to console in this case) is done outside the synchronized block. This is essential because otherwise other threads (MainClass) would be waiting for the lock during notify, which would reduce the liveliness of the threads.

MainClass.java
							
package com.threads; // Acts as an event generator public class MainClass { public static void main(String[] args) { // start the event thread EventThread eventThread = new EventThread(); eventThread.start(); for(int j = 0 ; j < 100 ; j++) { eventThread.notifyEvent(new Event(j,"tempUser")); } } }

The MainClass object acts as the event generator. It first starts the EventThread. After that it loops through to create 100 events as "tempUser".

When the program runs, it is expected to print all the console messages for events 0 to 99.

Problems & Solutions

The first problem that you see when you run the program is that not all the values from 0 to 99 are printed on the console. Sometimes none of the values are printed. This is a problem created because of missed notifications. The problem is that before the EventThread can call the wait method on the eventList object, some of the notifications might already have been sent from the MainClass. These notifications are the missed notifications, which the EventThread cannot recover (because wait() was not called before the notifications were sent). Since the wait() was called after the notifications were sent, the EventThread would be waiting even though there are events in the eventList object. In order to fix the problem, we can check the events list for any events before waiting.
							
if (eventList.isEmpty()) { eventList.wait(); }

This will make sure that if the eventList is not empty, then the thread will not wait, but process the messages. Eventually when all of the events in the list is processed, the wait() is called. When you make this change and run your program, everything seems to be fine and it prints all the events.

However, there is still a subtle issue about early notifications to remember. There may be rare cases when the wait() methods gets up from the sleep without a notification. These are known as spurious wakeups and some of the JVMs are known to do this. In addition, we need to protect the code from notifications from other parts of the code, which might notify without adding anything to the event list. (maybe due to bug in the code). Under these cases, the EventThread would fail while trying to retrieve a event from the eventList object. To avoid these problems, the 'if' should be changed to 'while'.
							
while (eventList.isEmpty()) { eventList.wait(); }

This makes sure that the eventList is not empty when it comes out of the while loop and hence the statement below will not fail with an exception.

							
event = (Event)eventList.remove(0);

The final code for EventThread.java is

							
package com.threads; import java.util.LinkedList; import java.util.List; public class EventThread extends Thread { // list of events private List eventList = new LinkedList(); ` public void notifyEvent(Event event) { synchronized (eventList) { eventList.add(event); eventList.notify(); } } public void run() { boolean stop = false; while(!stop) { try { Event event = null; synchronized(eventList) { while (eventList.isEmpty()) { eventList.wait(); } event = (Event)eventList.remove(0); } // Handle the event .. System.out.println("Event processed ID : " + event.getID()); } catch (InterruptedException iExp) { stop = true; } } } }

So, the general rule of thumb to use the wait statement is as follows. This idiom addresses the issue of both missed notifications and early notifications.
							
synchronized(<syncLock>) { while (<condition does not hold>) { syncLock.wait(); } // Perform action appropriate to condition } // Perform action that needs to be done outside the synchronized block

Conclusion

Although the wait/notify scheme seems to be easy to use, it can lead to subtle issues due to race conditions. So, it is essential that developers use the proper idiom to use the wait and notification. The idiom presented above solves the issues of missed notifications and early notifications.

References
  •  Java Thread Programming by Paul Hyde
  •  Effective Java Programming Language guide by Joshua Bloch