When should you use the wait() and notify method in Java is one of the many popular questions about the wait and notify methods from Java multithreading interview questions. One of the reasons for its popularity is that still a lot of Java programmers struggle to explain and write code using wait-notify methods. Many Java developer only knows some facts about the wait and notify methods like that wait() and notify() are defined in the java.lang.Object class or you cannot call wait() without synchronization, which means without a synchronized block or synchronized method but doesn't really know when and how to use them. In this article, we will try to bridge that gap by going through a simple example of classical Producer-Consumer problems and show you how you can use them while writing concurrent Java applications for the real world. First and foremost you should know that wait(), notify(), and notifyAll() are tools for inter-thread communication in Java. By using this method, you can tell a thread to stop working and later start processing. They are the essential building block of Java multi-threading applications. In the real world, you can use the wait() and notify() method to implement a producer-consumer pattern where most of the multi-threaded application falls. You can use the wait() and notify() method to communicate between multiple threads, for example, you can tell one thread to stop working from another thread based upon some condition, later you can notify it to start processing again. One of the popular examples of the wait() and notify() method is to solve the producer-consumer problem, where you can have either a single producer and a single consumer or multiple producers and a single consumer or just one producer and multiple consumers. In this example, you will learn how to implement multiple producers and single consumer solutions using wait() and notify() in Java.
Wait-Notify Example in Java for Producer-Consumer Problem
package tool;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.TimeUnit;
/**
* A simple Java Program to demonstrate how to use wait
* and notify() method ofr inter-thread communciation
* in Java.
*/
public class Hello {
public static void main(String[] args) {
Queue<String> q = new LinkedList<>();
boolean exit = false;
Producer p = new Producer(q, exit);
p.start();
Consumer c = new Consumer(q, exit);
c.start();
}
}
class Producer extends Thread {
private volatile Queue<String> sharedQueue;
private volatile boolean bExit;
public Producer(Queue<String> myQueue, boolean bExit) {
this.sharedQueue = myQueue;
this.bExit = bExit;
}
public void run() {
while (!bExit) {
synchronized (sharedQueue) {
while (sharedQueue.isEmpty()) {
String item = String.valueOf(System.nanoTime());
sharedQueue.add(item);
System.out.println("Producer added : " + item);
try {
System.out.println("Producer sleeping by calling wait:
" + item);
sharedQueue.wait();
System.out.println("Producer wake up: ");
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
class Consumer extends Thread {
private volatile Queue<String> sharedQueue;
private volatile boolean bExit;
public Consumer(Queue<String> myQueue, boolean bExit) {
this.sharedQueue = myQueue;
this.bExit = bExit;
}
public void run() {
while (!bExit) {
synchronized (sharedQueue) {
while (!sharedQueue.isEmpty()) {
String item = sharedQueue.poll();
System.out.println("Consumer removed : " + item);
System.out.println("Consumer notifying Producer: "
+ item);
sharedQueue.notify();
}
}
}
}
}
Output:
Producer added : 12275948008616
Producer sleeping by calling wait: 12275948008616
Consumer removed : 12275948008616
Consumer notifying Producer: 12275948008616
Producer wake up:
Producer added : 12275948047960
Producer sleeping by calling wait: 12275948047960
Consumer removed : 12275948047960
Consumer notifying Producer: 12275948047960
Producer wake up:
Producer added : 12275948082600
Producer sleeping by calling wait: 12275948082600
Consumer removed : 12275948082600
Consumer notifying Producer: 12275948082600
What's Happening Here?
Producer thread is producing items, adding them into a queue, and then going into wait() state until consumer thread consumes it. When the Consumer thread removes the items from the queue, it also notifies the Producer thread to start producing again. That's why you see an ordered output like Producer added, Producer Sleeping, Consumer Removed, Consumer Notified, and Producer Wakeup. In short, both Producer and Consumer Thread are talking with each other using the wait and notify method. If you remove that wait call then the Producer thread will keep checking for the queue to become waiting and keep wasting the CPU cycle. Similarly, if you remove the notify call then the waiting Producer thread may never wake up. Btw, if you have trouble understanding Producer-Consumer Problem then I also suggest taking a look at the Applying Concurrency and Multi-threading to Common Java Patterns course on Pluralsight.
Things to remember
1) Always call wait() from the synchronized context in Java 2) Always check the waiting condition in a loop instead of if block in Java 3) Remember, you can wake up a waiting thread by using the interrupt() method but only if your waiting thread handles the interrupted exception. 4) Prefer notifyAll() over notify() if you are in doubt 5) Don't forget to call the wait() and notify() method on the same shared object. That's all about when to use the wait and notify method in Java. It's a perfect and most native tool for inter-thread communication in Java. A good understanding of wait, notify, and the notifyAll method goes a long way in writing a robust and safe concurrent Java program. If you have any trouble understanding this problem, please drop a note and I'll try to explain.
Source: java67
The Tech Platform
Comments