Wait and Notify in Java – Example with images

In this article, we will cover wait and notify in Java programming language. We will talk about when to use wait and notify. We will see how each method handles the object monitor lock, when the monitor lock is released and when it is not released. In what scope should we use the wait and notify. Let us get started.

image01-The MainClass

How the MainClass works:

To explain how wait and notify in Java works we will use two Java classes, the MainClass, and the Executor class, let us start with the MainClass.  The images below are taken from the MainClass image above.  Please refer to the image above to see the whole MainClass code.  The images below are individual images of lines or blocks of code taken from MainClass.  The line numbers are at the left side of the images..

 

image02-main method call

 

Line 5 above starts the main method, nothing fancy here except that it throws an Interrupted Exception.

 

imaimage03-instantiation of Executor

 

Line 6, creates an instance of the class Executor, this object is called ex1, and it contains 2 methods, the sender method, and receiver method.

 

image04-threadA runnable

 

Lines 8 to 17. In this block of code, we instantiated a new thread object. The new Thread object is going to override his run method with the run method from the anonymous new Runnable class in line 8. The run method is implemented by lines 10 to 15. Lines 10 to 15 will call the sender method from the ex1 object, and, since the sender method throws an Interrupted Exception, the Interrupted Exception is handle with a catch. If the Exception is thrown, line 14 will print the stack trace.

 

image05-threadB runnable

 

Lines 19 to 28. This block is identical to the block on lines 8 to 16 with the exception that in line 23 we are calling the receiver method of the ex1 object, in line 12 instead we called the sender method of the ex1 object. Everything else in the block of code works the same as in the previous block of code (lines 8 to 16).

 

image06-starting threads

 

Lines 30 and 31. In these 2 lines, we start execution of the threads using the start method. Threads will go into the runnable state.

 

image07-joining threads

 

Lines 33 and 34. These 2 lines will cause the main method to wait for them to complete before executing any more code, once the threadA and threadB complete execution then the main method will continue executing.

 

How the Executor class works:

image08-first version of Executor

 

Here we have a bare bones class called Executor that contains only two methods, sender and receiver methods. The methods are prepared to throw Interrupted Exception.

 

Image09-Second version of Executor

 

Here we added a few lines of code to the Executor class:

 

image10-using synchronized block

 

Important:
The “this” object that we mention below is the current instance of the Executor class.  In other words it is the “ex1” object that was created(instantiated) in line 6 of the MainClass above.

Lines 8 to 11. In line 8 we acquire the lock from the “this” object. This means that if a thread obtains the lock, then no other thread will be able to run this code unless the lock is released. In line 9 we just print a message to the console. In line 10 we call the wait method. In here the wait method will:

  1.  Release the lock on the “this” object.
  2. The thread will not resume execution until:
    a. The thread regains control of the lock on the “this” object
    b. Another thread that has the lock on the same “this” object calls the method notify.

The Executor class:

 

image11-Executor class

 

Lines 15 to 27. Let us analyze the code in the receiver method. In line 16 we instantiate a new Scanner object call sc1. This object will be used to allow the user to press a key from the keyboard, in this case, the return key, and, this will resume the execution of the program. Line 17 puts the thread to sleep for 3 seconds allowing enough time for the sender method to start its logic first before the receiver method.  Line 18 starts a synchronized block on the object called this, which is the same object used in the synchronized block inside the sender method. In other words, both methods are synchronized using the same object monitor lock. The code in lines 19 to 24 haves a very close relationship with the code in lines 0 to 11. Here is the narrative:

  1. The MainClass calls first the sender method from threadA then the receiver method from threadB.
  2. Since we do not know which of the threads (threadA or threadB) is going to be moved from  runnable state to running state, the threadB receiver method haves a Thread.sleep(3000) instruction to pause execution in case it is executed before threadA. This way we make sure that
    threadA executes before threadB.
  3. When threadA starts running prints a message and goes to wait and releases the object “this” lock.
  4. ThreadB starts running the synchronized block. ThreadB acquires the lock from “this” that the wait statement in line 10 just released. ThreadB prints message from line 19.
  5. ThreadB starts waiting for the user to press the return key to continue the execution of the method.
  6. When the user presses the return key, threadB prints the message from line 21 and executes the notify. Notice the notify does not release the lock on object “this” immediately, the lock is released when the synchronized block of code finishes.
  7. Since the lock is not released by the notify method, the line 23 puts the thread to sleep for 6 seconds, and after 6 seconds then line 24 prints a message to console and the synchronized block finishes and releases the lock on object “this”. When the lock on “this” is released and since the notify method executed and “notified” threadA, then in threadA, the wait statement recovered the lock on “this” and then the threadA finishes the wait, and goes to next statement.
  8. Line 11 is executed by printing a message to the console.

Result of running the program:

image12-Results of running the program

Key points to remember with Wait and
Notify in Java

___wait and notify are supposed to be used only into a synchronized block of code.

___Wait will release the lock on the object specified by the synchronized keyword.

___Wait can specify a timeout so that it does not stay waiting indefinitely.

___Wait is very efficient and does not consume lots of resources.

___Notify will not release the lock on the object specified by the synchronized keyword.

___Every object in Java haves a wait method, the wait method is part of the Object class, which is the ancestor of all objects.

___To use wait and notify in harmony, you must acquire the lock of the same object in the method that calls wait, and in the method that calls notify.

___Normally after you call notify you want to relinquish control of the object lock very quickly, so that the wait statement can acquire control of the lock.

___There is a notifyAll method.

Source Code MainClass

package wait.and.notify.v02;

public class MainClass {

    public static void main(String[] args) throws InterruptedException {

        final Executor ex1 = new Executor();

        Thread threadA = new Thread(new Runnable() {

            @Override

            public void run() {

                try {

                    ex1.sender();

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }

            }

        });

        Thread threadB = new Thread(new Runnable() {

            @Override

            public void run() {

                try {

                    ex1.receiver();

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }

            }

        });

        threadA.start();

        threadB.start();

        threadA.join();

        threadB.join();

    }

}

Source Code Executor Class

package wait.and.notify.v02;

import java.util.Scanner;

public class Executor {

    public void sender() throws InterruptedException {

        synchronized (this) {

            System.out.println(“The sender method is running……”);

            wait();

            System.out.println(“Resuming the sender method…..”);

        }

    }

    public void receiver() throws InterruptedException {

        Scanner sc1 = new Scanner(System.in);

        Thread.sleep(3000);

        synchronized (this) {

            System.out.println(“Press return key to continue…..”);

            sc1.nextLine();

            System.out

                    .println(“You pressed the return key… continuing with the next line…”);

            notify();

            Thread.sleep(6000);

            System.out.println(“Finished with execution of receiver method.”);

        }

    }

}

 

Leave a Comment