When and How to use the wait() and notify(), nofityAll() methods in Java? - Example Tutorial

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 programmer 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, 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 Problem 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. If you don't know much about them, I suggest you join a fundamental course on Java threads like Threading Essentials Mini-Course by Java Champion, Heinz Kabutz.




How to use the wait() and notify() methods in Java

You can use 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 example of wait() and notify() method is to solve the producer-consumer problem, where you can have either single producer and single consumer or multiple producers and 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. If you are not familiar with notify and notifyAll method, you can further see The Complete Java Masterclass to learn more.

How to use the wait() and notify() methods in Java


Wait-Notify Example To Solve 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 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 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 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.

When and How to use the wait() and notify(), nofityAll() metohd in Java?



Things to remember

1) Always call wait() from the synchronized context in Java, see here to learn why

2) Always check the waiting condition in a loop instead of if block in Java, see here to learn why.

3) Remember, you can wake up a waiting thread by using interrupt() method but only if your waiting thread handles the interrupted exception.

4) Prefer notifyAll() over notify() if you are in doubt, see here to learn more.

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.

Further Learning
The Complete Java Masterclass
Applying Concurrency and Multi-threading to Common Java Patterns
Java Concurrency in Practice - The book
Java Concurrency in Practice Course by Heinz Kabutz
50+ Java Multithreading Interview Questions in Java
How to avoid deadlock in Java programs
Top 5 Courses to Learn Multithreading and Concurrency in Java
Multithreading and Parallel Computing in Java


Thanks for reading this article so far. If you like this article then please share with your friends and colleagues. If you have any questions or feedback, please drop a note. 

No comments:

Post a Comment