How to work with wait(), notify() and notifyAll() in Java?

Java concurrency is pretty complex topic and requires a lot of attention while writing application code dealing with multiple threads accessing one/more shared resources at any given time. Java 5, introduced some classes like BlockingQueue and Executors which take away some of the complexity by providing easy to use APIs.

Programmers using concurrency classes will feel a lot more confident than programmers directly handling synchronization stuff using wait(), notify() and notifyAll() method calls. I will also recommend to use these newer APIs over synchronization yourself, BUT many times we are required to do so for various reasons e.g. maintaining legacy code. A good knowledge around these methods will help you in such situation when arrived.

In this tutorial, I am discussing the purpose of wait() notify() notifyall() in Java. We will understand the difference between wait and notify.

Read more : Difference between wait() and sleep() in Java

1. What are wait(), notify() and notifyAll() methods?

The Object class in Java has three final methods that allow threads to communicate about the locked status of a resource.

  1. wait()

    It tells the calling thread to give up the lock and go to sleep until some other thread enters the same monitor and calls notify(). The wait() method releases the lock prior to waiting and reacquires the lock prior to returning from the wait() method. The wait() method is actually tightly integrated with the synchronization lock, using a feature not available directly from the synchronization mechanism.

    In other words, it is not possible for us to implement the wait() method purely in Java. It is a native method.

    General syntax for calling wait() method is like this:

    synchronized( lockObject )
    { 
    	while( ! condition )
    	{ 
    		lockObject.wait();
    	}
    	
    	//take the action here;
    }
    
  2. notify()

    It wakes up one single thread that called wait() on the same object. 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.

    General syntax for calling notify() method is like this:

    synchronized(lockObject) 
    {
    	//establish_the_condition;
    
    	lockObject.notify();
    	
    	//any additional code if needed
    }
    
  3. notifyAll()

    It wakes up all the threads that called wait() on the same object. The highest priority thread will run first in most of the situation, though not guaranteed. Other things are same as notify() method above.

    General syntax for calling notify() method is like this:

    synchronized(lockObject) 
    {
    	establish_the_condition;
    
    	lockObject.notifyAll();
    }
    
In general, a thread that uses the wait() method confirms that a condition does not exist (typically by checking a variable) and then calls the wait() method. When another thread establishes the condition (typically by setting the same variable), it calls the notify() method. The wait-and-notify mechanism does not specify what the specific condition/ variable value is. It is on developer’s hand to specify the condition to be checked before calling wait() or notify().

Let’s write a small program to understand how wait(), notify(), notifyall() methods should be used to get desired results.

2. How to use with wait(), notify() and notifyAll() methods

In this exercise, we will solve producer consumer problem using wait() and notify() methods. To keep program simple and to keep focus on usage of wait() and notify() methods, we will involve only one producer and one consumer thread.

Other features of the program are :

  • Producer thread produce a new resource in every 1 second and put it in ‘taskQueue’.
  • Consumer thread takes 1 seconds to process consumed resource from ‘taskQueue’.
  • Max capacity of taskQueue is 5 i.e. maximum 5 resources can exist inside ‘taskQueue’ at any given time.
  • Both threads run infinitely.

2.1. Producer Thread

Below is the code for producer thread based on our requirements :

class Producer implements Runnable
{
   private final List<Integer> taskQueue;
   private final int           MAX_CAPACITY;

   public Producer(List<Integer> sharedQueue, int size)
   {
      this.taskQueue = sharedQueue;
      this.MAX_CAPACITY = size;
   }

   @Override
   public void run()
   {
      int counter = 0;
      while (true)
      {
         try
         {
            produce(counter++);
         } 
		 catch (InterruptedException ex)
         {
            ex.printStackTrace();
         }
      }
   }

   private void produce(int i) throws InterruptedException
   {
      synchronized (taskQueue)
      {
         while (taskQueue.size() == MAX_CAPACITY)
         {
            System.out.println("Queue is full " + Thread.currentThread().getName() + " is waiting , size: " + taskQueue.size());
            taskQueue.wait();
         }
		  
         Thread.sleep(1000);
         taskQueue.add(i);
         System.out.println("Produced: " + i);
         taskQueue.notifyAll();
      }
   }
}
  • Here “produce(counter++)” code has been written inside infinite loop so that producer keeps producing elements at regular interval.
  • We have written the produce() method code following the general guideline to write wait() method as mentioned in first section.
  • Once the wait() is over, producer add an element in taskQueue and called notifyAll() method. Because the last-time wait() method was called by consumer thread (that’s why producer is out of waiting state), consumer gets the notification.
  • Consumer thread after getting notification, if ready to consume the element as per written logic.
  • Note that both threads use sleep() methods as well for simulating time delays in creating and consuming elements.

2.2. Consumer Thread

Below is the code for consumer thread based on our requirements :

class Consumer implements Runnable
{
   private final List<Integer> taskQueue;

   public Consumer(List<Integer> sharedQueue)
   {
      this.taskQueue = sharedQueue;
   }

   @Override
   public void run()
   {
      while (true)
      {
         try
         {
            consume();
         } catch (InterruptedException ex)
         {
            ex.printStackTrace();
         }
      }
   }

   private void consume() throws InterruptedException
   {
      synchronized (taskQueue)
      {
         while (taskQueue.isEmpty())
         {
            System.out.println("Queue is empty " + Thread.currentThread().getName() + " is waiting , size: " + taskQueue.size());
            taskQueue.wait();
         }
         Thread.sleep(1000);
         int i = (Integer) taskQueue.remove(0);
         System.out.println("Consumed: " + i);
         taskQueue.notifyAll();
      }
   }
}
  • Here “consume()” code has been written inside infinite loop so that consumer keeps consuming elements whenever it finds something in taskQueue.
  • Once the wait() is over, consumer removes an element in taskQueue and called notifyAll() method. Because the last-time wait() method was called by producer thread (that’s why producer is in waiting state), producer gets the notification.
  • Producer thread after getting notification, if ready to produce the element as per written logic.

2.3. Test producer consumer example

Now lets test producer and consumer threads.

public class ProducerConsumerExampleWithWaitAndNotify
{
   public static void main(String[] args)
   {
      List<Integer> taskQueue = new ArrayList<Integer>();
      int MAX_CAPACITY = 5;
      Thread tProducer = new Thread(new Producer(taskQueue, MAX_CAPACITY), "Producer");
      Thread tConsumer = new Thread(new Consumer(taskQueue), "Consumer");
      tProducer.start();
      tConsumer.start();
   }
}

Program Output.

Produced: 0
Consumed: 0
Queue is empty Consumer is waiting , size: 0
Produced: 1
Produced: 2
Consumed: 1
Consumed: 2
Queue is empty Consumer is waiting , size: 0
Produced: 3
Produced: 4
Consumed: 3
Produced: 5
Consumed: 4
Produced: 6
Consumed: 5
Consumed: 6
Queue is empty Consumer is waiting , size: 0
Produced: 7
Consumed: 7
Queue is empty Consumer is waiting , size: 0

I will suggest you to change the time taken by producer and consumer threads to different times, and check the different outputs in different scenario.

3. Interview questions on wait(), notify() and notifyAll() methods

3.1. What happens when notify() is called and no thread is waiting?

In general practice, this will not be the case in most scenarios if these methods are used correctly. Though if the notify() method is called when no other thread is waiting, notify() simply returns and the notification is lost.

Since the wait-and-notify mechanism does not know the condition about which it is sending notification, it assumes that a notification goes unheard if no thread is waiting. A thread that later executes the wait() method has to wait for another notification to occur.

3.2. Can there be a race condition during the period that the wait() method releases OR reacquires the lock?

The wait() method is tightly integrated with the lock mechanism. The object lock is not actually freed until the waiting thread is already in a state in which it can receive notifications. It means only when thread state is changed such that it is able to receive notifications, lock is held. The system prevents any race conditions from occurring in this mechanism.

Similarly, system ensures that lock should be held by object completely before moving the thread out of waiting state.

3.3. If a thread receives a notification, is it guaranteed that the condition is set correctly?

Simply, no. Prior to calling the wait() method, a thread should always test the condition while holding the synchronization lock. Upon returning from the wait() method, the thread should always retest the condition to determine if it should wait again. This is because another thread can also test the condition and determine that a wait is not necessary — processing the valid data that was set by the notification thread.

This is a common case when multiple threads are involved in the notifications. More particularly, the threads that are processing the data can be thought of as consumers; they consume the data produced by other threads. There is no guarantee that when a consumer receives a notification that it has not been processed by another consumer.

As such, when a consumer wakes up, it cannot assume that the state it was waiting for is still valid. It may have been valid in the past, but the state may have been changed after the notify() method was called and before the consumer thread woke up. Waiting threads must provide the option to check the state and to return back to a waiting state in case the notification has already been handled. This is why we always put calls to the wait() method in a loop.

3.4. What happens when more than one thread is waiting for notification? Which threads actually get the notification when the notify() method is called?

It depends on many factors.Java specification doesn’t define which thread gets notified. In runtime, which thread actually receives the notification varies based on several factors, including the implementation of the Java virtual machine and scheduling and timing issues during the execution of the program.

There is no way to determine, even on a single processor platform, which of multiple threads receives the notification.

Just like the notify() method, the notifyAll() method does not allow us to decide which thread gets the notification: they all get notified. When all the threads receive the notification, it is possible to work out a mechanism for the threads to choose among themselves which thread should continue and which thread(s) should call the wait() method again.

3.5. Does the notifyAll() method really wake up all the threads?

Yes and no. All of the waiting threads wake up, but they still have to reacquire the object lock. So the threads do not run in parallel: they must each wait for the object lock to be freed. Thus, only one thread can run at a time, and only after the thread that called the notifyAll() method releases its lock.

3.6. Why would you want to wake up all of the threads if only one is going to execute at all?

There are a few reasons. For example, there might be more than one condition to wait for. Since we cannot control which thread gets the notification, it is entirely possible that a notification wakes up a thread that is waiting for an entirely different condition.

By waking up all the threads, we can design the program so that the threads decide among themselves which thread should execute next. Another option could be when producers generate data that can satisfy more than one consumer. Since it may be difficult to determine how many consumers can be satisfied with the notification, an option is to notify them all, allowing the consumers to sort it out among themselves.

Happy Learning !!

Was this post helpful?

Join 7000+ Fellow Programmers

Subscribe to get new post notifications, industry updates, best practices, and much more. Directly into your inbox, for free.

37 thoughts on “How to work with wait(), notify() and notifyAll() in Java?”

  1. Hi Lokesh, How to terminate all those threads, after, let’s say, all 30 items where “produced” and “consumed”? Thank you.

  2. Threads are objects so they have lock like other objects. When we use wait() and notify() in synchronized methods, which lock do we release? Thread’s or the object sources which thread use?

    • @mimi, calling wait and notify release object’s lock. Thread don’t have lock associated with it, object’s have lock with it that’s why we call wait(), notify() method on object reference and not thread reference.

  3. This is an excellent article. However, with the above code I get a very orderly production, until the queue is full, and a very orderly consumption, until the queue is empty, and then this repeats.

    The example would be much better if the Thread.sleep() were placed outside the synchronized block, and randomized for a different timeout each time, say, between 500ms and 1000ms.

    The critical section (synchronized block) is simply too large, and by the time the block ends, it repeats immediately and therefore the same thread is likely to reacquire the lock immediately, thus the long strings of production and consumption, without much interleaving. Move the sleep outside and randomize it, and you will have a much better example.

  4. Thank you for the blog!
    I have a question:

    Why do we need to write `while (taskQueue.size() == MAX_CAPACITY)` ?
    Can we use `if` instead of `while`?

  5. Hi,
    Thanks for such a wonderful post.
    In your example both consumer and producer can’t run parallel right?
    To produce or consume they require lock. So if producer is running, consumer can’t process any entry even if the queue is not empty.

  6. Hello Lokesh,my problem is when i run the program post in your blog ,the output is regular,the consumer do not wake up until the queue is full,can you help me?tks
    Produced: 0
    Produced: 1
    Produced: 2
    Produced: 3
    Produced: 4
    Queue is full Producer is waiting , size: 5
    Consumed: 0
    Consumed: 1
    Consumed: 2
    Consumed: 3
    Consumed: 4
    Queue is empty Consumer is waiting , size: 0
    Produced: 5
    Produced: 6
    Produced: 7
    Produced: 8
    Produced: 9
    Queue is full Producer is waiting , size: 5
    Consumed: 5
    Consumed: 6
    Consumed: 7
    Consumed: 8
    Consumed: 9
    Queue is empty Consumer is waiting , size: 0
    Produced: 10
    Produced: 11
    Produced: 12
    Produced: 13
    Produced: 14
    Queue is full Producer is waiting , size: 5
    Consumed: 10
    Consumed: 11
    Consumed: 12
    Consumed: 13
    Consumed: 14
    Queue is empty Consumer is waiting , size: 0
    Produced: 15
    Produced: 16

    • To have the process interleave at all I needed to add a Thread.currentThread().yield(); just inside the while (true) loops of the Consumer and Producer.

  7. Hi Lokesh,
    Can u tell me that what r the conditions to get the object lock. Means can we lock the object through synchronised method and what is the role of wait() method for getting the objects lock.

  8. See i was really devasted and could not understand how to explain this concept in practical scenarios you showed how easily it can be explained

  9. Hello Lokesh,
    I implemented the code mentioned in the blog and mentioned below is the output I was getting

    Produced: 0
    Produced: 1
    Produced: 2
    Produced: 3
    Produced: 4
    Queue is full Producer is waiting , size: 5
    Consumed: 0
    Consumed: 1
    Consumed: 2
    Consumed: 3
    Consumed: 4
    Queue is empty Consumer is waiting , size: 0
    Produced: 5
    Produced: 6
    Produced: 7
    Produced: 8
    Produced: 9
    Queue is full Producer is waiting , size: 5
    Consumed: 5
    Consumed: 6
    Consumed: 7
    Produced: 10
    Produced: 11
    Produced: 12
    Queue is full Producer is waiting , size: 5
    Consumed: 8
    Consumed: 9
    ... 

    Now, the question :
    Q.1
    I hoped for this output only, not the output mentioned in the mentioned in blog as synchronization is on taskQueue (which is being passed from static void main() method) in both the class. So what happens when tProducer.start() happens. It gets the lock of taskQueue object and starts producing and then releases the lock when wait is called. At this time tConsumer which was was in waiting for the lock of taskQueue object in consume() method to get into synchronized block and goes into consuming the item from list and when size is zero it goes into wait and releases the lock and tProducer acquires it and this keeps on going. Am I correct with this understanding ?
    Q.2 I am not able to get what does this “taskQueue.wait()” means in his code. What would have happened if I happen if I use “wait()” or “notifyAll()” only, when I tried it I got this as output after running for a while.

    Consumed: 4
    Queue is empty Consumer is waiting , size: 0
    Exception in thread "Consumer" java.lang.IllegalMonitorStateException
    at java.lang.Object.wait(Native Method)
    at java.lang.Object.wait(Object.java:503)
    at in.shubham.howtodoinjava.Consumer.consume(Consumer.java:38)
    at in.shubham.howtodoinjava.Consu...(Consumer.java:23)
    at java.lang.Thread.run(Thread.java:745)
    • 1) You are right.
      2) wait() and notifyAll() must be called on some object, which producer and consumer both can access. So rather than creating a new monitor object for this purpose, I used this existing object.
      Regarding error, not sure how you have modified the wait() and notify() calls. Please share the modified code.

      • Hello Lokesh,

        Firstly thanks for the clearing my doubt. And secondly, I just wrote like this in Producer and Consumer

        this.wait();
           and
         this.notifyAll(); 
      • Thanks for such a quick response and clearing my doubt.
        and what I did in my code was “this.notifyAll()” and “this.wait()” INSTEAD of “taskQueue.wait()” or “taskQueue.notifyAll()”.

        Now that you have told that “wait() and notifyAll() must be called on some object, which producer and consumer both can access” , I somehow think I am able to figure out why error came but it would be really nice if you can elaborate it or what this IllegalMonitorStateException means ?

        • You can’t wait() on an object unless the current thread owns that object’s monitor – otherwise IllegalMonitorStateException is thrown. To own the monitor of that object, you must synchronize on it.

          e.g. So if you want to use this.wait() which I do not recommend, you must do it inside synchronized(this) {...} block.

  10. Thanks for the post. It cleared up many of the confusions I had about threading in general. I just have one quick question about the notify() method. In Producer class after you add(produce) the product into the LinkedList you call notify(), is this to ‘wake up’ the Consumer Thread or Producer Thread? and if it’s for consumer Thread, why so ?

    • I only ask this question because my understanding of notify() is “it wakes up a thread that is waiting on this object’s moniotr”. Thus, since Producer has been put on “waiting” when the list is full, it has to be notified by notify()method within its body to start working again. Is this wrong?

  11. when notify method is called which thread is revoked execution has two ambiguous answers.

    1) definition of notify() : It wakes up the first thread that called wait() on the same object

    2) answer of question: What happens when more than one thread is waiting for notification? Which threads actually get the notification when the notify() method is called?

    Could you please tell me which one is correct.

  12. Nice article.
    It cleared many of my doubts regarding multithreading.

    synchronized (taskQueue)
          {
             while (taskQueue.size() == MAX_CAPACITY)
             {
                System.out.println(&quot;Queue is full &quot; + Thread.currentThread().getName() + &quot; is waiting , size: &quot; + taskQueue.size());
                taskQueue.wait();
             }
               
    

    Like here in above code you associated wait() diirectly on taskqueue object.

    https://www.java-samples.com/showtutorial.php?tutorialid=306
    On this link,wait() is not written like this.wait(). Is wait() directly got link with q object.

  13. really good..most of other learning forums have explained the concept..but here you have put in very simple way. the approach of taking consumer from one Thread class object and Producer as different Thread class object simplified the things and I am able to understand quickly…keep posting..Thanks a lot…

  14. HI,
    Thanks for such an wonderful post. But recently, a question asked to my friend. So, can you help me with that.

    1) Write a small program to create dead lock with wait() and notify().
    2) Write a small programm to create dead lock with synchronixed block().

    If possible please answer.

  15. “By waking up all the threads, we can design the program so that the threads decide among themselves which thread should execute next”
    Can you please explain me how it can be done ? How can the threads decide among themselves ?

    • An example could be group of threads (one thread processing only a specific type of message) watching a message queue. Upon placing a message in queue, producer can notify all consumer threads. Then each consumer thread may check if message is for it, and if it is then process it otherwise wait again.

      This design may look inefficient as correct thread may get chance to check in last; BUT it will start making sense when you start adding more message types and their handler threads into system without modifying code of other handlers.

      Just for an example. Make sense?

  16. I think in this example instead of List, Queue should be used.

    Else each time only element @ location zero will be consumed only.

    • I appreciate the article very much!
      So I’d like to argue for it. Maybe a queue is better to understand here, but there is no doubt that a list can be used as a queue in a proper way (append to rear, get&remove from front), as what the article did. For your question, a consumer indeed consumes only element @location zero each time, and this is also the meaning of First Out for a queue.

Comments are closed.

HowToDoInJava

A blog about Java and its related technologies, the best practices, algorithms, interview questions, scripting languages, and Python.