Open In App

Inter-thread Communication in Java

Prerequisite: Multithreading in Java, Synchronized in Java 

Inter-thread communication in Java is a mechanism in which a thread is paused running in its critical section and another thread is allowed to enter (or lock) in the same critical section to be executed.



Note: Inter-thread communication is also known as Cooperation in Java.

What is Polling, and what are the problems with it? 



The process of testing a condition repeatedly till it becomes true is known as polling. Polling is usually implemented with the help of loops to check whether a particular condition is true or not. If it is true, a certain action is taken. This wastes many CPU cycles and makes the implementation inefficient. 

For example, in a classic queuing problem where one thread is producing data, and the other is consuming it.

How Java multi-threading tackles this problem? 

To avoid polling, Java uses three methods, namely, wait(), notify(), and notifyAll(). All these methods belong to object class as final so that all classes have them. They must be used within a synchronized block only. 

Example:

A simple Java program to demonstrate the three methods. Please note that this program might only run in offline IDEs as it contains taking input at several points.




// Java program to demonstrate inter-thread communication
// (wait(), join() and notify())
  
import java.util.Scanner;
  
public class threadexample
{
    public static void main(String[] args) throws InterruptedException
    {
        final PC pc = new PC();
  
        // Create a thread object that calls pc.produce()
        Thread t1 = new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                try
                {
                    pc.produce();
                }
                catch(InterruptedException e)
                {
                    e.printStackTrace();
                }
            }
        });
  
        // Create another thread object that calls
        // pc.consume()
        Thread t2 = new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                try
                {
                    pc.consume();
                }
                catch(InterruptedException e)
                {
                    e.printStackTrace();
                }
            }
        });
  
        // Start both threads
        t1.start();
        t2.start();
  
        // t1 finishes before t2
        t1.join();
        t2.join();
    }
  
    // PC (Produce Consumer) class with produce() and
    // consume() methods.
    public static class PC
    {
        // Prints a string and waits for consume()
        public void produce()throws InterruptedException
        {
            // synchronized block ensures only one thread
            // running at a time.
            synchronized(this)
            {
                System.out.println("producer thread running");
  
                // releases the lock on shared resource
                wait();
  
                // and waits till some other method invokes notify().
                System.out.println("Resumed");
            }
        }
  
        // Sleeps for some time and waits for a key press. After key
        // is pressed, it notifies produce().
        public void consume()throws InterruptedException
        {
            // this makes the produce thread to run first.
            Thread.sleep(1000);
            Scanner s = new Scanner(System.in);
  
            // synchronized block ensures only one thread
            // running at a time.
            synchronized(this)
            {
                System.out.println("Waiting for return key.");
                s.nextLine();
                System.out.println("Return key pressed");
  
                // notifies the produce thread that it
                // can wake up.
                notify();
  
                // Sleep
                Thread.sleep(2000);
            }
        }
    }
}

Output
producer thread running
Waiting for return key.
Return key pressed
Resumed

Explanation:

As monstrous as it seems, it is a piece of cake if you go through it twice. 

  1. In the main class, a new PC object is created.
  2. It runs produce and consume methods of PC objects using two different threads, namely t1 and t2, and waits for these threads to finish.

Let’s understand how our produce and consume method works. 

If you are still confused as to why we have used notify in consume thread, try removing it and running your program again, as you must have noticed now that the program never terminates. 

The reason for this is straightforward. When you called to wait on the produce thread, it went on waiting and never terminated. Since a program runs till all its threads have terminated, it runs on and on. 

There is a second way around this problem. You can use a second variant of wait(). 

void wait(long timeout) 

This would make the calling thread sleep only for a time specified. 


Article Tags :