This article will explain what is interthread communication in java, how to do it using wait(), notify() and notifyAll() methods with examples, explanations and some important interview questions regarding these in between.

InterThread communication
As the name suggests, this refers to the interaction among threads. Let us understand why it is required.

Suppose there is a worker thread performing some operation and one or more threads want the result of this operation and thus, they should not execute till worker thread finishes.
This requires some method of signaling so that when one thread finishes, it informs other waiting threads.
Java multithreading provides wait(), notify() and notifyAll() methods which simplify such thread interaction.
First we look at an example and then dig deeper into method definitions.
Problem
When you purchase an item on a website, after selecting the item, you are asked for payment. You need to enter your card details, then an OTP is sent to your mobile and so on.
Finally, when the payment is completed, only then you see a confirmation page that you have bought the item.
Consider the page asking for card details asĀ  Thread1 and the one that completes the payment as Thread2.

Would it be correct, if Thread1 asks for payment details and immediately shows the confirmation page while the payment(Thread2) is still in progress?

Obviously not…. There are multiple reasons
1. Might be you entered incorrect details by mistake.
2. OTP did not arrive due to network error or wrong OTP was entered.
3. You changed your mind and not entered OTP.
Example code is given below.

class PaymentProcessor implements Runnable{
  
   @Override
   public void run() {
      System.out.println("Preparing payment");
      try {
         System.out.println("Waiting for payment completion");
         // assume it is accepting payment
         Thread.sleep(5000);
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
      System.out.println("Payment done successfully");
   }
}
public class Main {
   public static void main(String[] args) throws InterruptedException {
     System.out.println("Asking for payment");
     PaymentProcessor p = new PaymentProcessor();
     Thread t1 = new Thread(p);
     System.out.println("Calling payment thread");
     t1.start();
     System.out.println("*** Order is confirmed ***");
   }
}

In this code, PaymentProcessor class is responsible for accepting payment which is run in a separate thread. main() method creates an object of this class and starts a new thread.
Note the sleep() method in PaymentProcessor simulates a time taking task such as waiting for OTP.
Below is the output

Asking for payment
Calling payment thread
*** Order is confirmed ***
Preparing payment
Waiting for payment completion

Look, order is confirmed before payment confirmation. This system will always confirm the order irrespective of payment status.

Ideal scenario is
1. Thread1 should wait till Thread2 completes,
2. Thread2 should inform Thread1.

Below is the modified program which uses wait() and notify() methods to implement this correctly.

class PaymentProcessor implements Runnable{
  
   @Override
   public void run() {
      System.out.println("Preparing payment");
      synchronized (this) {
         try {
            System.out.println("Waiting for payment confirmation");
            Thread.sleep(5000);
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
         System.out.println("Payment done successfully");
         this.notify();
      }
   }	
}
public class Application {
   public static void main(String[] args) throws InterruptedException {
      System.out.println("Asking for payment");
      PaymentProcessor p = new PaymentProcessor();
      Thread t1 = new Thread(p);
      t1.start();
      synchronized (p) {
         System.out.println("Waiting for payment completion");
         p.wait();
      }
      System.out.println("*** Order is confirmed ***");
   }
}

It produces following output

Asking for payment
Waiting for payment completion
Preparing payment
Waiting for payment confirmation
Payment done successfully
*** Order is confirmed ***

Main thread calls wait() as soon as it starts the second thread. It keeps on waiting till the second thread calls notify() after which main thread resumes.
Explanation of all the three methods is given below which will further clarify the concept.

wait()
When an executing thread calls wait() on an object, its execution is paused till some other thread calls notify() or notifyAll() on the same object.
As soon as wait() is called, thread enters Waiting state and when notify() is called, thread enters Runnable state.
wait() can only be invoked from a synchronized context(from synchronized method or block). Thread should have the lock of object on which wait() is called.
Thus, below code will throw an IllegalMonitorStateException.

PaymentProcessor p = new PaymentProcessor();
PaymentProcessor p1 = new PaymentProcessor();
synchronized (p) {
   p1.wait();		
}

Because the thread has a lock of p while it calls wait() on p1. This is an important interview question.

There are two overloaded methods for wait() as below
A. wait(long timeout)
No argument wait() will keep on waiting indefinitely and may cause the application to hang. If this is not what you want, then use this method.
It takes a timeout value and the thread will wait for the supplied milliseconds and after that will resume execution.
B. wait(long timeout, int nano)
This method provides a more granular timeout in which you can supply timeout upto nano seconds after which the thread will resume execution.

Remember that a thread immediately gives up the lock of the object as soon as wait() is called. This makes sense since each object has only one lock.
If the thread will not release the lock, then how will another thread invoke notify() on the same object. This is also an interview question.
notify()
notify() is invoked on an object to inform a waiting thread that it can resume execution.
Remember that notify() should be called on the same object on which wait() was called otherwise the thread will remain in Waiting state only.
To call notify() on an object, the current executing thread should own its lock and this is the reason that notify() can only be called from a synchronized context.
When notify() is called, the thread does not immediately release the object’s lock. Another interview question.
Reason is that it might need to perform some other tasks after calling notify() and there may be some statements written inside the synchronized block, therefore it can not immediately release the lock.
notifyAll()
There may be more than one waiting threads on an object. If notify() is called, only one of the waiting threads will resume execution while others will keep on waiting.
If you want that all the threads begin execution, then notifyAll() should be used.
If there are multiple waiting threads and notify() is called, which thread will be notified so that it begins execution.
This is another interview question and the answer is that this is random and depends on Thread scheduler. Any thread may be notified.
As with notify(), notifyAll() can also be called from a synchronized context only.

Object class
Remember that wait(), notify() and notifyAll() methods are defined in java.lang.Object class and not java.lang.Thread class though they are only related to multithreading only.
Reason for this is that these methods may be called on any object, be it a builtin object such as String, Integer or a user defined object such as Cat, Dog, Book etc.
Since these methods should be available to all the classes, there is only one place where they should be defined, the super class of all objects.
This is an important interview question.
Hope you liked the article.