Sunday, September 17, 2023

How to use wait, notify and notifyAll in Java - Producer Consumer Example

You can use wait, notify, and notifyAll methods to communicate between threads in Java. For example, if you have two threads running in your programs like Producer and Consumer then the producer thread can communicate to the consumer that it can start consuming now because there are items to consume in the queue. Similarly, a consumer thread can tell the producer that it can also start putting items now because there is some space in the queue, which is created as a result of consumption. A thread can use the wait() method to pause and do nothing depending upon some condition. For example, in the producer-consumer problem, the producer thread should wait if the queue is full and the consumer thread should wait if the queue is empty.

If some thread is waiting for some condition to become true, you can use notify and notifyAll methods to inform them that the condition is now changed and they can wake up.

Both notify() and notifyAll() method sends a notification but notifies sends the notification to only one of the waiting thread, no guarantee which thread will receive notification and notifyAll() sends the notification to all threads.

So if only one thread is waiting for an object lock, also known as a monitor then both notify and notifyAll will send the notification to it. If multiple threads are waiting on a monitor then notify will only inform one of the lucky threads and the rest will not receive any notification, but notifyAll will inform all threads.

In this Java multi-threading tutorial, you will learn how to use the wait, notify, and notifyAll() method in Java to implement inter-thread communication by solving the producer-consumer problem.

And, if you are serious about mastering Java multi-threading and concurrency then I also suggest you take a look at these best Java Concurrency courses on  Udemy. It's an advanced course to become an expert in Multithreading, concurrency, and Parallel programming in Java with a strong emphasis on high performance






How to use wait and notify in code?

Even though wait and notify are quite fundamental concepts and they are defined in the object class, surprisingly, it's not easy to write code using wait and notify. You can test this during an interview by asking the candidate to write code to solve the producer-consumer problems using wait and notify by hand.

I am sure many will be stuck and make mistakes e.g. synchronizing at the wrong place, not calling wait on a right object, or not following standard idiom. To be honest it's confusing for non-regular coders.

First confusion arises from the fact, how to call the wait() method? Since the wait method is not defined in the Thread class, you cannot simply call Thread.wait(), that won't work but since many Java developers are used to calling Thread.sleep() they try the same thing with wait() method and stuck.

You need to call the wait() method on the object which is shared between two threads, in the producer-consumer problem it's the queue that is shared between producer and consumer threads.

The second confusion comes from the fact that wait needs to be a call from a synchronized block or method? So if you use a synchronized block, which object should be put to go inside the block? This should be the same object, whose lock you want to acquire i.e. the shared object between multiple threads. In our case, it's a queue.

How to use wait and notify method in Java with example



Always call wait and notify from Loop Instead of If Block

Once you know that you need to call wait from synchronized context and on the shared object, the next thing is to avoid mistakes made by several Java developers by calling the wait() method inside if block instead of the while loop.

Since you call wait inside a conditional block e.g. producer thread should call wait() if queue is full, first instinct goes towards using if block, but calling wait() inside if block can lead to subtle bugs because it's possible for a thread to wake up spuriously even when waiting condition is not changed.

If you don't check the condition again after waking up by using a loop, you will take the wrong action which may cause problems e.g. trying to insert an item on a full queue or trying to consume from an empty queue. That's why you should always call the wait and notify method from a loop and not from if block.

I also suggest reading the Effective Java item on the same topic, probably the best reference in how to properly call the wait and notify method.




Based upon the above knowledge here is  the standard code template or idiom to call the wait and notify method in Java :

// The standard idiom for calling the wait method in Java
synchronized (sharedObject) {
   while (condition) {
      sharedObject.wait(); // (Releases lock, and reacquires on wakeup)
   }
   ... // do action based upon condition e.g. take or put into queue
}

As I suggested, you should always invoke the wait method from a loop. The loop is used to test the condition before and after waiting. If the condition still holds and the notify (or notifyAll) method has already been invoked before a thread calls wait for () method, then there is no guarantee that the thread will ever awake from the wait, potentially causing a deadlock.

BTW, if you are serious about mastering concurrency and multi-threading, I strongly suggest you read Java Concurrency in Practice by Brian Goetz, without reading that book your journey to Java multi-threading is not complete. It's probably one of the most recommended books to Java developers.





Java wait(), notify() and notifyAll() Example - Producer Consumer Pattern Example

Here is our sample program to demonstrate how to use the wait and notify method in Java. In this program, we have used the standard idiom discussed above to call wait(), notify(), and notifyAll() method in Java.

In this program, we have two threads named PRODUCER and CONSUMER and implemented using Producer and Consumer class which extends Thread class. The logic of what the producer and the consumer should do is written in their respective run() method.

The main thread starts both producer and consumer threads and also creates an object of LinkedList class to share as Queue between them. If you don't know LinkedList also implements a Queue interface in Java.

Producer runs in an infinite loop and keeps inserting random integer values into Queue until the queue is full. We check this condition at while(queue.size == maxSize), remember before doing this check we synchronize on queue object so that no other thread modifies the queue when we are doing this check.

If Queue is full then our PRODUCER thread waits until CONSUMER thread consumes one item and makes space in your queue and calls notify() method to inform PRODUCER thread. Both wait() and notify() methods are called on shared object which is a queue in our case.

import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;

/**
 * Simple Java program to demonstrate How to use wait, notify and notifyAll()
 * method in Java by solving producer consumer problem.
 * 
 * @author Javin Paul
 */
public class ProducerConsumerInJava {

    public static void main(String args[]) {
        System.out.println("How to use wait and notify method in Java");
        System.out.println("Solving Producer Consumper Problem");
        
        Queue<Integer> buffer = new LinkedList<>();
        int maxSize = 10;
        
        Thread producer = new Producer(buffer, maxSize, "PRODUCER");
        Thread consumer = new Consumer(buffer, maxSize, "CONSUMER");
        
        producer.start();
        consumer.start();
        

    }

}

/**
 * Producer Thread will keep producing values for Consumer
 * to consumer. It will use wait() method when Queue is full
 * and use notify() method to send notification to Consumer
 * Thread.
 * 
 * @author WINDOWS 8
 *
 */
class Producer extends Thread {
    private Queue<Integer> queue;
    private int maxSize;
    
    public Producer(Queue<Integer> queue, int maxSize, String name){
        super(name);
        this.queue = queue;
        this.maxSize = maxSize;
    }
    
    @Override
    public void run() {
        while (true) {
            synchronized (queue) {
                while (queue.size() == maxSize) {
                    try {
                        System.out .println("Queue is full, "
                                + "Producer thread waiting for "
                                + "consumer to take something from queue");
                        queue.wait();
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                }

                Random random = new Random();
                int i = random.nextInt();
                System.out.println("Producing value : " + i);
                queue.add(i);
                queue.notifyAll();
            }

        }
    }
}

/**
 * Consumer Thread will consumer values form shared queue.
 * It will also use wait() method to wait if queue is
 * empty. It will also use notify method to send 
 * notification to producer thread after consuming values
 * from queue.
 * 
 * @author WINDOWS 8
 *
 */
class Consumer extends Thread {
    private Queue<Integer> queue;
    private int maxSize;
    
    public Consumer(Queue<Integer> queue, int maxSize, String name){
        super(name);
        this.queue = queue;
        this.maxSize = maxSize;
    }
    
    @Override
    public void run() {
        while (true) {
            synchronized (queue) {
                while (queue.isEmpty()) {
                    System.out.println("Queue is empty,"
                            + "Consumer thread is waiting"
                            + " for producer thread to put something in queue");
                    try {
                        queue.wait();
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }

                }
                System.out.println("Consuming value : " + queue.remove());
                queue.notifyAll();
            }

        }
    }
}

Output
How to use wait and notify method in Java
Solving Producer Consumper Problem
Queue is empty,Consumer thread is waiting for producer thread 
to put something in queue
Producing value : -1692411980
Producing value : 285310787
Producing value : -1045894970
Producing value : 2140997307
Producing value : 1379699468
Producing value : 912077154
Producing value : -1635438928
Producing value : -500696499
Producing value : -1985700664
Producing value : 961945684
Queue is full, Producer thread waiting for consumer
 to take something from queue
Consuming value : -1692411980
Consuming value : 285310787
Consuming value : -1045894970
Consuming value : 2140997307
Consuming value : 1379699468
Consuming value : 912077154
Consuming value : -1635438928
Consuming value : -500696499
Consuming value : -1985700664
Consuming value : 961945684
Queue is empty,Consumer thread is waiting for producer 
thread to put something in queue
Producing value : 1182138498

In order to understand this program better, I suggest you debug it instead of running it. Once you start your program in debug mode it will stop at either PRODUCER or CONSUMER thread, depending upon which one thread scheduler chose to give CPU.

Since both threads have wait conditions they will go there, now you just run it and see what it does, it will most likely print the output shown above. You can even use the Step Into and Step Over buttons in Eclipse to run the program step by step to understand it better.




Things to Remember about Using wait(), notify() and notifyAll() method 

  1. You can use the wait() and notify() method to implement inter-thread communication in Java. Not just one or two threads but multiple threads can communicate to each other by using these methods.
  2. Always call wait(), notify() and notifyAll() methods from synchronized method or synchronized block otherwise JVM will throw IllegalMonitorStateException.
  3. Always call wait and notify method from a loop and never from if() block, because loop test waiting condition before and after sleeping and handles notification even if waiting for the condition is not changed.
  4. Always call wait in shared object e.g. shared queue in this example.
  5. Prefer notifyAll() over notify() method due to reasons given in this article. 

Producer consumer solution in Java using wait and notify


That's all about how to use the wait, notify, and notifyAll() method in Java. You should use wait and notify for inter-thread communication in Java only if you know what you are doing otherwise there are many high-level concurrency utilities available for the different tasks.

For example, if you want to implement a producer-consumer pattern then you can use BlockingQueue which will manage both thread-safety and flow control for you if you want your thread should wait for other threads before proceeding you can use CyclicBarrier or CountDownLatch or if you want to protect resource you can use Semaphore.


Other Java Multithreading and Concurrency tutorials you may like
  • What is happens-before in Java concurrency? (answer)
  • How to use Future and FutureTask class in Java? (solution)
  • Difference between volatile, atomic, and synchronized in Java (answer)
  • How to use Semaphore class in Java? (code sample)
  • How to use the Lock interface in multi-threaded programming? (code sample)
  • How to use Thread pool Executor in Java? (example)
  • How to use a CyclicBarrier class in Java? (example)
  • 50 Java Thread Questions for Senior and Experienced Programmers (questions)
  • How to use the CountDownLatch utility in Java? (example)
  • Difference between CyclicBarrier and CountDownLatch in Java? (answer)
  • how to do inter-thread communication using wait and notify? (solution)
  • How to use ThreadLocal variables in Java? (example)
  • Top 5 Concurrent Collection classes from Java 5 and Java 6 (read here)
  • 10 Examples of ConcurrentHahsMap in Java (examples)
  • 10 Multi-threading and Concurrency Best Practices for Java Programmers (best practices)

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

P.S. - If you are a Java beginner and need a free course to learn essential Java thread concepts then I highly suggest you join this free Java Multithreading course for beginners. This is the best free online course to learn Java Multithreading from scratch. 

And, lastly one question for you? What is the best way to solve producer consumer problem in Java? wait and notify method, BlockingQueue or SynchronousQueue? If you know the answer let us know in comments. 

28 comments :

Anonymous said...

I'm really shocked why here is no comment on outstanding example and explanation? :O

I want to ask, there is no guarantee which thread will start first? consumer or producer?

javin paul said...

@Anonymous, well you are the first to comment :). Yes, there is no guarantee which thread will start, but it doesn't matter because if producer will start first it will start putting object, but if consumer will start, it will just wait until object is available for consumption.

Anonymous said...

Very good example. If I change maxsize to 1 will it work like, producer will produce one item then consumer will consume that item.....and it continues...
Or is there any other way to achieve what I want?

-Unmesh

Anonymous said...

The simplest way to solve the producer consumer problem is by using Blocking-queue, as explained in this tutorial, but if you really want to understand the core concept behind producer consumer pattern then you must solve this problem using wait and notify method in Java. when you solve it hard way, you learn more.

ρєєуυѕн ρяαтαρ said...

In above example, consumer comes into picture only when the producer has filled up the queue upto its max size. Similarly, the producer starts producing only after the queue is fully consumed. This should not be the case.
The concept that this problem is based on, is that the producer and consumer should be able to work separately. That means the consumer is free to start consuming once the queue becomes non-empty (no matter the producer has produced 1 item or maximum items). Similarly producer should be free to start producing as soon as the queue size becomes lesser than the max size. It should no wait for the consumer to consume the whole queue.

javin paul said...

Hi, that's not true. Both Producer and Consumer are running in parallel. If your assumption is based upon the output than it's wrong because threads are running really fast, try introducing sleep after adding and removing elements from linkedlist (the shared queue) and debug the program in Eclipse. you will see both threads are working in parallel, waiting for locks, check condition and process as expected.

here is the output of this program when I debugged in Eclipse:

How to use wait and notify method in Java
Solving Producer Consumper Problem
Queue is empty,Consumer thread is waiting for producer thread to put something in queue
Producing value : 1849895202
Consuming value : 1849895202
Queue is empty,Consumer thread is waiting for producer thread to put something in queue
Producing value : -1274906234
Consuming value : -1274906234

Let me know if you still have any doubt.

Anonymous said...

The code works correctly as Javin says. The reason is that the queue.wait() RELEASES the intrinsic lock so that the other thread can modify the queue object (enqueue or dequeue).

Sidak said...

Brilliant. Thanks!

Unknown said...

Good post Thanks!!

Unknown said...

very useful post.. Thanks

Anonymous said...

Producing value ..2145701772
Producing value ..-1583439136
Producing value ..-1496055580
Producing value ..1680454426
Producing value ..-755744647
Producing value ..-2092974764
Producing value ..880553434
Producing value ..62086747
Producing value ..1833223884
Producing value ..2079999503
List is full ..
List is full ..

This is output for me.Its not entering the consumer class.Why is that?

Sumit said...

i have tried the above problem with multiple producers and consumers . so, i am really confused how can we work on that one.

Unknown said...

When there is many producer and consumers it might cause race condition. Let's assume there is 2 consumer and 1 producer. With bad luck 2 producers call remove and there is check then act and NoSuchElementException is thrown.

Tasneem Hyder said...

Very nice post!! Well presented. Thanks.

Sanghmitra said...

Why adding item to queue in produce and removing item from queue in consume not in synchronised block (along with the notifies)? I think it should have been.

munjal said...

Hello sir,
Thank you for publishing such a great article.
I am new to java world, and I really like your post.

Here we have taken only one producer thread and one consumer thread.
In my application , I need to create more than one producer and consumers.
In such cases I can not write such a big synchronized block , as if producer-1 is creating Item-1 , producer-2 can not take lock on queue and so two producers can not produce symultaniously.

same problem for consumer also.

can you suggest the solution here ?

please suggest some book also which is full of such cases.

Thank you.

javin paul said...

Hello Munjal, instead of writing code using wait and noitfy, you should just use BlockingQueue, you don't have to synchronize anything, Queue will ensure that consumer threads will wait if queue is empty and producer threads will wait if queue is full. This can support multiple producer and consumer as well. See here for an implementation.

Regarding books, you can check here

munjal said...

why you have user notifyAll() here ? we have only two threads here which are interdependent.

javin paul said...

Hello @munjal, this is to ensure that code should work fine with just one producer/consumer or multiple producers and consumers. You are right in the sense that in our program we have just one producer and consumer and we can definitely use notify() instead of notifyAll() but our code doesn't prevent anything from creating more than one producer or consumer, if you use notify, then that can create issues in future. If you are 100% sure that you were not going to have multiple producer or consumer, you can use notify method.

Anonymous said...

Program is not proper. Its executing Producer and Consumer at the same time.

javin paul said...

Hello @Anonymous, what is the problem? Can you please tell us bit more?

Anonymous said...

Hi Javin, Why can't we write notifyAll() right after queue.wait()?

javin paul said...

Hello Anonymous, actually notify() is outside loop. Idea is you check for a condition and if condition is for waiting then you wait e.g. if bucket is empty then wait, if not then its time to consume items and then notifying producer thread which might be waiting because queue was full.

Unknown said...

superb post, Thanks a ton

Anonymous said...

I have to agree with Piyush Partap
"In above example, consumer comes into picture only when the producer has filled up the queue upto its max size. Similarly, the producer starts producing only after the queue is fully consumed. This should not be the case.
The concept that this problem is based on, is that the producer and consumer should be able to work separately. That means the consumer is free to start consuming once the queue becomes non-empty (no matter the producer has produced 1 item or maximum items). Similarly producer should be free to start producing as soon as the queue size becomes lesser than the max size. It should no wait for the consumer to consume the whole queue."

.
.
I tried the same already with sleep methods for both the threads.

biniam said...

what a fantastic clear explanation thanks

Anonymous said...

Hi. Thank you very much for your detailed explanation. You put great emphasis on important things. Still unfortunately I could not get what exactly notify() and notifyAll() do? What kind of notification do they send? Is it that they just make the code quit the synchronized block while we still remain in infinite loop? Then how and when we enter synchronized block again? And another problem not clear for me - does it produce a problem that notify() chooses the thread to pass the monitor to chaotically? So does it mean that we have no control what thread will run next? What's the use of that chaotic concept? Thank you

javin paul said...

Hello Anonymous, its good that you ask as lot of people don't understand this concept but don't ask due to fear of looking bad, which is not correct, you should ask no matter how simple the question. Coming back to your question,

What exactly notify and notifyAll() do?
When a thread called wait() on an object like myObject.wait(), they will stop right there and will not proceed unless another thread will call myObject.notify() or myObject.notifyAll() method, so notify and notifyAll() essential make the threads waiting for notification to start again.

What kind of notification do they send?
If you notice above code, they are called on same object, I mean myObject.wait() and myObject.notify(), both must be called on same object, because each object owns a lock and if you call on other object, thread waiting for that lock will be notified not the thread waiting for myObject lock. So the send notification to release lock associated with that object on which they are called

They don't make to quit synchronized block or remain in while loop, its the code you write, all they do is send notification so that threads waiting for that lock can also come to Runnable state and then when scheduler choose they can run again.

Next of your question related to difference between notify() and notifyAll(), since its possible that more than one thread can call myObject.wait(), Java provides you option to notify one of those thread or all of those thread, its up to you how you implement it.

If you know that only one thread is waiting then you have full control that notify() will work because its you who have coded the waiting part. If you have coded in such a way that more thread is waiting on same object then yes, you will not have control on which thread will be notified, hence this kind of coding is discouraged.

I hope these answers will help you to learn and understand waiting and notify in Java better, feel free to ask if you still have doubts.

Post a Comment