Close

Java - Thread Communication using wait/notify

[Last Updated: Feb 7, 2017]

It is common in multithreaded programming to require one thread to wait for another thread to take some action.

Semantically, there's no direct thread communication possible as per underlying thread model, instead a developer can use some condition based on a shared variable to achieve the inter-thread communication.

Let's consider following example where the thread1 prints a message while thread2 populates the same message string:

public class SimpleWaitNotifyDemo {
    private static String message;

    public static void main (String[] args) {

        Thread thread1 = new Thread(() -> {
            System.out.println(message);
        });

        Thread thread2 = new Thread(() -> {
            message = "A message from thread1";
        });

        thread1.start();
        thread2.start();
    }
}

Due to thread scheduler unpredictability, we cannot guarantee that thread1 will print the message populated by thread2. There must be some mechanism for thread1 to signal thread2 that shared message has been populated. Following is the most common solution a developer can come up with:

package com.logicbig.example;

public class SimpleWaitNotifyDemo {
    private static String message;

    public static void main (String[] args) {

        Thread thread1 = new Thread(() -> {
            while (message == null) {
            }

            System.out.println(message);
        });

        Thread thread2 = new Thread(() -> {
            message = "A message from thread1";
        });

        thread1.start();
        thread2.start();
    }
}

The above thread1's while loop is known as 'Guarded Blocks', but that's not an efficient solution as this loop is just wasting CPU cycles also we cannot avoid thread interference .

Java extends it's 'intrinsic lock' model to provide a better solution, i.e. by using java.lang.Object methods wait() and notify()/notifyAll().

All these Object's method calls are redirected to native methods, that means this mechanism is inherently provided by underlying operating system.

Let's modify above example to see how that works:

public class SimpleWaitNotifyDemo {
    private static String message;

    public static void main (String[] args) {
        Object lock = new Object();

        Thread thread1 = new Thread(() -> {
            synchronized (lock) {
                while (message == null) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }

            System.out.println(message);
        });

        Thread thread2 = new Thread(() -> {
            synchronized (lock) {
                message = "A message from thread1";
                lock.notify();
            }
        });

        thread1.start();
        thread2.start();
    }
}


Object.wait() and Object.notify()

wait() causes the current thread to wait (blocks or suspends execution) until another thread invokes the notify() method or the notifyAll() method for this object.

wait() method throws InterruptedException - If the corresponding thread calls interrupt() then the waiting state will get interrupted (just like sleep() method). The interrupted status of the current thread is cleared when this exception is thrown.

Unlike sleep method which doesn't release the 'intrinsic lock', this method releases the 'intrinsic lock'. That means other threads have opportunity to execute other synchronized blocks/methods while this thread is in waiting state, which is good for overall performance.

Also this method call will throw IllegalMonitorStateException if the current thread is not the owner of the object's monitor (the intrinsic lock). That's the reason we used synchronized block in the above example.

In above example, we can also use synchronized methods instead. Let's modify above example to consolidate synchronization and thread communication to a single object:

public class WaitNotifyDemo {

    public static void main (String[] args) {
        SharedObject obj = new SharedObject();

        Thread thread1 = new Thread(() -> {
            obj.printMessage();
        });

        Thread thread2 = new Thread(() -> {
            obj.setMessage("A message from thread1");
        });

        thread1.start();
        thread2.start();
    }

    private static class SharedObject {
        private String message;

        public synchronized void setMessage (String message) {
            this.message = message;
            notify();
        }

        public synchronized void printMessage () {
            while (message == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(message);

        }
    }
}



notify() vs notifyAll()

notify() wakes up a single thread that is waiting on this object's monitor (intrinsic lock). If multiple threads are waiting then only one of them will get notified or will wake up; which thread will be notified, we cannot predict, that totally depends on the scheduler.

notifyAll() will cause all threads to wake up if they are in waiting state due to wait() call.

Since notify() and notifyAll() are also called from synchronized method/blocks: waiting thread will not start executing till the thread which calls these methods releases the lock.

Example Project

Dependencies and Technologies Used:

  • JDK 1.8
  • Maven 3.0.4

Thread Communication Example Select All Download
  • java-thread-wait-notify
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • SimpleWaitNotifyDemo.java

    See Also