How do I use the wait() and notify() methods?
Author: Deron Eriksson
Description: This Java tutorial describes how to synchronize lists and other collections in Java.
Tutorial created using: Windows Vista || JDK 1.6.0_11 || Eclipse JEE Ganymede SR1 (Eclipse 3.4.1)


The Object class in JavaSW has three final methods that allow threads to communicate about the locked status of a resource. These methods are wait(), notify(), and notifyAll(). A thread obtains a lock for a particular resource via a synchronized block with an instance of that resource. Suppose that a thread requires that another thread perform a certain action on the resource before it acts on the resource. That thread can synchronize on the resource and call the wait() method on resource. This says that the thread will wait until it has been notified that it can proceed to act. The wait() method can take an optional timeout value as a parameter. If this value is used, it means that the thread will either wait until it's notified or it will continue to execute once the timeout value has passed.

If a thread is required to perform a task on a resource before another thread operates on the resource (and the other thread is waiting via the wait() method on the resource), the thread needs synchronize on the resource. It can perform its actions on the resource. In order to notify the waiting thread once these actions have completed, the notify() method on the resource is called. This notifies the waiting thread that it can proceed to act. If multiple threads are waiting for the resource, there is no guarantee as to which thread will be given access to the resource. If it is desired for all waiting threads to be awoken, the notifyAll() method can be called on the resource.

Let's look at an example of this. First, let's create a Message class. An instance of this class will be utilized by a waiting thread and a notifying thread.

Message.java

package com.cakes;

public class Message {

	private String text;

	public Message(String text) {
		this.text = text;
	}

	public String getText() {
		return text;
	}

	public void setText(String text) {
		this.text = text;
	}

}

The Waiter class implements Runnable. A Waiter object will wait for a Notifier object to complete operations on a Message object. This is done via a synchronized block on the Message object, in which we call wait() on the Message object.

Waiter.java

package com.cakes;

import java.util.Date;

public class Waiter implements Runnable {

	Message message;

	public Waiter(Message message) {
		this.message = message;
	}

	@Override
	public void run() {
		synchronized (message) {
			try {
				System.out.println("Waiter is waiting for the notifier at " + new Date());
				message.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println("Waiter is done waiting at " + new Date());
		System.out.println("Waiter got the message: " + message.getText());
	}

}

The Notifier class implements Runnable. The Notifier object sleeps for 3 seconds. After this, it modifies the message and then notifies the waiting thread that it can now continue. This is done in a synchronized block (synchonized on the Message object) and then by calling notify() on the Message object.

Notifier.java

package com.cakes;

import java.util.Date;

public class Notifier implements Runnable {

	Message message;

	public Notifier(Message message) {
		this.message = message;
	}

	@Override
	public void run() {
		System.out.println("Notifier is sleeping for 3 seconds at " + new Date());
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}
		synchronized (message) {
			message.setText("Notifier took a nap for 3 seconds");
			System.out.println("Notifier is notifying waiting thread to wake up at " + new Date());
			message.notify();
		}

	}

}

The WaitNotifyExample class creates a Message object. It creates a Waiter object with the Message object. It creates and starts a thread with the Waiter object. After this, it creates a Notifier object with the Message object. It creates and starts another thread with the Notifier object.

WaitNotifyExample.java

package com.cakes;

public class WaitNotifyExample {

	public static void main(String[] args) {

		Message message = new Message("Howdy");

		Waiter waiter = new Waiter(message);
		Thread waiterThread = new Thread(waiter, "waiterThread");
		waiterThread.start();

		Notifier notifier = new Notifier(message);
		Thread notifierThread = new Thread(notifier, "notifierThread");
		notifierThread.start();

	}

}

The console output from the execution of WaitNotifyExample is shown here. From the output, notice that the waiter thread waits while the notifier thread has the lock on the message object. When the notifier thread notifies the waiter thread that it is done, the waiter thread's execution can proceed.

Console Output

Waiter is waiting for the notifier at Tue May 19 17:52:57 PDT 2009
Notifier is sleeping for 3 seconds at Tue May 19 17:52:57 PDT 2009
Notifier is notifying waiting thread to wake up at Tue May 19 17:53:00 PDT 2009
Waiter is done waiting at Tue May 19 17:53:00 PDT 2009
Waiter got the message: Notifier took a nap for 3 seconds

One nice thing about wait/notify is that it avoids polling (checking over and over to see if a resource is ready to be used), since polling can be expensive in terms of system resources.

It should be noted that calling notify() does not actually give up a lock on a resource. It tells a waiting thread that that thread can wake up. However, the lock is not actually given up until the notifier's synchronized block has completed. So, if a notifier calls notify() on a resource but the notifier still needs to perform 10 seconds of actions on the resource within its synchronized block, the thread that had been waiting will need to wait at least another additional 10 seconds for the notifier to release the lock on the object, even though notify() had been called.