Java wait-notify-notifyAll Tutorial

In this section, we will learn what the wait(), notify(), and notifyAll() methods are and how they work in Java.

Note: we’re assuming you’re already familiar with the Thread Synchronization in Java.

What is wait() method in Java?

An object’s monitor has two sets:

  • Entry-Set
  • Waiting-Set

The entry-set is the place where those threads that just entered into a synchronized method/block want to execute the body of that method/block. Here, only one of them will acquire the monitor’s lock and the rest will be blocked in the `entry-set` until the target thread releases the lock.

Note that when a thread fully runs a block/method and then exits, we say the state of the thread is `release and exit`.

Now, the waiting-set is the place where a thread that is currently acquired the lock of a monitor temporarily releases that lock and goes into this waiting-set. A thread might temporarily release a thread because there’s a condition that must occur before the thread can proceed to run the rest of instructions of the same synchronized method/block. This means the thread is not done yet in the target method/block! So it temporarily releases the acquired lock, allowing other threads to acquire the lock and then at some point in the future, when the condition occurred, the temporarily blocked thread will re-acquire the lock of the monitor. (We will explain how a thread can re-acquire a lock in just a moment!).

Now, a thread to release the lock of a monitor, it needs to call a method named `wait()`.

The `wait()` method belongs to the Object class and we can only use this method in a synchronized method/block! Also, the thread that is calling the wait() method should’ve already acquired the lock of the target object’s monitor! Otherwise, we will get an exception called “InterruptedException”! So when using this method, we need to make sure to handle this exception as well.

Also, if the wait() method is called in a block or method that is not synchronized, it will throw another exception that is called “IllegalMonitorStateException”.

Alright, just to clear things up a bit, if a thread wants to temporarily release the lock of a monitor to allow other threads to run the same method/block, it can call the `wait()` method. After that, the thread will be moved to a set of the target monitor that is known as the `Waiting-Set`. Also note that, when a thread calls this method, its state becomes `release and waiting`, which means the thread is not out of the target synchronized block/method yet, but it is just waiting for a condition to occur.

Another important aspect about calling the `wait()` method is that, when a thread calls this method, it is true that it will be blocked for quite sometimes, but the moment it reacquired the lock of the same method/block, it will continue to run the rest of instructions right after the `wait()` statement that it ran previously! This means it won’t start from the first statement of that method/block again!

Note: as we will explain in the rest of this section, we use the notify() or notifyAll() method to awaken a thread or multiple threads in the waiting-set of a monitor.

An example of why one thread may need to release a lock, we can refer to the consumer/producer thread synchronization.

You’ll see an example of this producer/ consumer later in this section, but just to give you an insight of how this works: consider two threads, one named `consumer` and the other is `producer`. Also, there’s a shared variable (for example and instance variable) of type int that both threads have the access to. Here, the producer thread is in charge of filling the shared variable and the consumer thread is in charge of taking out the value that the producer thread set for the variable. Now, if the producer thread wanted to fill the variable but found out that the variable is not consumed by the consumer thread yet, it should call the wait() method to put itself in the waiting-set of the target monitor. Now, the producer thread will wait until the consumer thread comes in and take the value of the shared variable and call the `notify()` method to awaken the producer thread.

Note that the same scenario works for the consumer thread as well! Basically, if the consumer thread came in a synchronized method to take the value of a shared variable but it saw that the shared variable is still empty (perhaps the producer thread was busy doing something else), it can call the `wait()` method to release the acquired lock and let the producer thread take control and fill the variable. Now, when the producer thread filled the value, it should call the notify() method to awaken the consumer thread and let it consume the value of the variable.

Alright, before we get into the details of how to practically use the `wait()` method, first let’s jump into the syntax of this method and see its structure.

Syntax of wait() method in Java:

There are three versions of the `wait()` method:

  • wait()
  • wait(long timeout)
  • wait (long timeout, int nanos)

The first version of this method declares that if a thread called this method, it should be blocked in the `waiting-set` of the target monitor and wait until another thread that acquired the lock of the same object’s monitor, calls either notify() or notifyAll() method of the same object.

The second version of the wait() method puts a thread in the waiting-set of the target object’s monitor for a period that we declare as the argument of this method in milliseconds. Now, if the period of time elapsed and no thread called the `notify()` or `notifyAll()` method to awaken the thread, then the thread will automatically wake up and it starts to compete with other threads that may want to acquire the lock of the monitor. But note that if another thread that currently had the lock of the monitor called the `notify()` or `notifyAll()` method, then the thread that was waiting for a period will automatically wake up (before the time elapses) and starts to compete with the rest of threads for the lock of the monitor. So the value we set as the argument of the wait() method defines the `maximum` amount of time that a thread can wait until it wakes up.

The third version of the `wait()` method is pretty much the same as the second version, with the exception that it takes a second argument as well. This second argument is used if we want to be more precise with the amount of time a thread should wait in the waiting-set! Note that the second argument declared the amount of time in nanoseconds.

What is notify() and notifyAll() in Java?

So far, we know that when a thread wants to temporarily relinquish a lock, it needs to call the `wait()` method. But, now that the thread is in the waiting-set of the target monitor, how can it be awoken again? Well, the answer is the notify() and notifyAll() methods.

These methods can be used by a thread that is currently holding the lock of the same monitor that has the threads in its waiting-set.

  • If the current thread calls the `notify()` method, it will cause one of the threads in the waiting-set to be awake and start to compete with other threads to see if it can take the CPU’s time next.
  • If the current thread calls the `notifyAll()` method, it will cause the entire threads in the waiting-set to be awake and they all will compete with each other to take the CPU’s time after the current thread went out and finished its work (or if the current thread released the lock by calling the wait() method and put itself in the waiting-set of the target monitor).

Note that calling the notify() or notifyAll() method doesn’t guarantee which thread in the waiting-set of the target monitor will take the lock of the monitor next! Basically, this is a signal to the waiting threads that they can now be prepared to take the lock next, but it’s the job of the JVM to choose which thread can take the lock! (It might even choose a thread from the entry-set instead!).

Also, note that when a thread calls either `notify()` or `notifyAll()` method, the thread itself will continue to hold on the lock of the monitor until it either finishes the work and exit from the method/block, or call the wait() method and put itself in the waiting-set.

Java Thread notify() and notifyAll() method Syntax

These are the syntaxes of the notify() and notifyAll() methods:

public final void notify()

public final void notifyAll()

Note that if a thread calls these methods, but the thread was not the owner of the object’s monitor lock, we will get the IllegalMonitorStateException exception. So, only the thread that currently has the ownership of the lock can call the method.

Example: creating producer/consumer threads using wait() and notify() methods

public class Main {
    public int count = 0;
    public static void main(String[] args) {
        Main main = new Main();
        Thread consumer = new Thread(main::consumer, "Consumer-Thread");
        Thread producer = new Thread(main::producer, "Producer-Thread");

        consumer.start();
        producer.start();

    }
    public synchronized void consumer(){
        count = 1;
        while (count != 0) {

            System.out.println("The current value of the count variable is: "+count);
            count = 0;
            notify();
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    public synchronized void producer(){
        for (int i = 0 ; i<10; i++){
            if (count == 0){
                int ii = new Random().nextInt();
                count =  ii== 0? 1:ii;
                notify();
                try {
                    Thread.sleep(1000);
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }else{
                try {
                    Thread.sleep(1000);
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        notify();
        count = 0;
        System.out.println("End");
    }
}

Output:

The current value of the count variable is: 1

The current value of the count variable is: 389466255

The current value of the count variable is: -1926883990

The current value of the count variable is: -39538155

The current value of the count variable is: -905287141

The current value of the count variable is: -1573799878

The current value of the count variable is: -455899447

The current value of the count variable is: 124050708

The current value of the count variable is: -81541598

The current value of the count variable is: 8675242

The current value of the count variable is: 1485185283

End

How do wait() and notify() methods work in Java?

In the last example, there are two threads, named `producer` and `consumer`.

The producer thread is in charge of the `producer` method and the `consumer` thread is in charge of running the `consumer` method.

Here, both threads start to run the body of the specified methods.

Now, within the body of the producer method, we declared a for-loop to run 10 times and per each iteration, the value of the count variable will be checked against the value 0. If the value is 0, that means the consumer thread already consumed the value of the variable and so the producer can proceed to update the variable with a new value. So, here the producer thread sets a new value for the variable and then first calls the `notify()` method and then the `wait()` method to put itself in the waiting-set of the `main` object’s monitor. (Note that the moment the producer thread calls the wait() method, it will release the lock of the monitor and the thread and its state will be saved in the waiting-set).

This will give a chance to the `consumer` thread to wake up and acquire the lock of the monitor and continue to run the body of the `consumer()` method.

The body of the `consumer()` method is the place where the thread tries to read the value of the count variable and send it to the output stream, it then calls the notify() method to signal the `producer` thread to be ready to take the lock, and after that the consumer thread calls the `wait()` method to put itself in the waiting-set and relinquish the lock of the monitor, allowing the producer thread to start running.

Now, this process goes on and on until 10 iterations of the for-loop in the producer thread is done. After that, the `producer` thread sets the value of the `count` variable to 0, calls the `notify()` method to prepare the `consumer` thread to take the lock and exits from the `producer` method, hence makes the lock of the monitor available to be taken by the `consumer` thread.

FAQ:

What’s the difference between notify() and notifyAll() methods in java?

Calling the `notify()` method will only awaken one thread in the waiting-set of the target monitor. But calling the notifyAll() method causes the entire threads in the waiting-set of the monitor to start competing with each other to take the lock of the target object’s monitor.

Note: depending on the underlying structure of the JVM, calling the notify() or notifyAll() method, the JVM might choose a thread from either of the available threads in the entry-set or waiting-set of the target object’s monitor.

What’s the difference between sleep() and wait() methods?

The sleep() method causes the target thread to wait for a specific period of time without releasing the lock that it acquired from an object’s monitor. This means, while the thread is waiting for that period, no-other threads can acquire the same lock!

On the other hand, when a thread calls the wait() method, it basically releases the lock it acquired and goes into the `waiting-set` of the target object’s monitor (the one that acquired its lock previously). So here, by making a thread to call the wait() method, it gives a chance to other threads to acquire the same lock and execute the same method/block. Now, a waited thread should wait until it is awoken by another thread (by calling either notify() or notifyAll() method) or if that method is waiting based on a time period, it then waits until that time period is passed.

Leave a Reply