Monday, 10 September 2018

Interthread Communication

Java provides inter-thread communication using the following methods of the Object class.
  • wait()
  • notify()
  • notifyAll()

These methods wait(), notify() and notifyAll() are implemented as final method in the Object class thus available to all the classes, as Object class is the super class of all the classes in Java.
wait(), notify() and notifyAll() methods are in Object class  not in Thread class
The following are the reasons behind having these methods in Object class.
  • First of all we have to know what wait() and notify() do in order to be clear why these methods are in Object class.
    • wait - wait method tells the current thread to give up monitor and go to sleep. 
    • notify - Wakes up a single thread that is waiting on this object's monitor.
    So you see wait() and notify() methods work at the monitor level, thread which is currently holding the monitor is asked to give up that monitor through wait() method and through notify() method (or notifyAll) threads which are waiting on the object's monitor are notified that threads can wake up. Important point to note here is that monitor is assigned to an object not to a particular thread. That's one reason why these methods are in Object class. To reiterate threads wait on an Object's monitor (lock) and notify() is also called on an object to wake up a thread waiting on the Object's monitor.
  • wait(), notify() and notifyAll() are used for inter-thread communication. But threads themselves have no knowledge of each others status. It is the shared object among the threads that acts as a communicator among the threads. Threads lock an object, wait on an object and notify an object. When a wait method is called it checks which thread has the lock on the object and that is the thread which has to give up the lock. Same way notify() method when called looks for all the thread that are waiting to get hold of the Object's monitor and wakes one of the thread, notifyAll() wakes up all the thread that are waiting on an Object's monitor. So it is the shared object among the thread which allows them to communicate with each other and wait(), notify() and notifyAll() are the methods used for inter-thread communication.
Another important point about wait(), notify() and notifyAll() methods in Java is that they can only be called from a synchronized context, as these methods are about releasing the monitor and acquiring it again. Threads acquire monitor(lock) when entering a synchronized method (or block) so it makes sense to call them from synchronized context.
The following are the reasons why  wait(), notify() and notifyAll() must be called inside a synchronized method or block:
  • Every object created in Java has one associated monitor (mutually exclusive lock). Only one thread can own a monitor at any given time.
  • For achieving synchronization in Java this monitor is used. When any thread enters a synchronized method/block it acquires the lock on the specified object. When any thread acquires a lock it is said to have entered the monitor. All other threads which need to execute the same shared piece of code (locked monitor) will be suspended until the thread which initially acquired the lock releases it.
  • wait method tells the current thread (thread which is executing code inside a synchronized method or block) to give up monitor and go to waiting state.
  • notify method wakes up a single thread that is waiting on this object's monitor.
  • notifyAll method wakes up all the threads that called wait() on the same object.

wait() method in Java

Wait method tells the current thread (thread which is executing code inside a synchronized method or block) to give up monitor and go to sleep, until another thread invokes the notify() or notifyAll() method for this object.
General form of wait method in Java

  • public final void wait() throws InterruptedException
There are two more overloaded wait methods
  • public final void wait(long timeout) throws InterruptedException : 
  • Causes the current thread to wait until either another thread invokes the notify() method or the notifyAll() method for this object, or a specified amount of time has elapsed.
  • public final void wait(long timeout, int nanos) throws InterruptedException:
  • Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object, or some other thread interrupts the current thread, or a certain amount of real time has elapsed.

notify() method in Java

Wakes up a single thread that is waiting on this object's monitor. If more than one threads are waiting on this object, one of them is chosen to be awakened. The choice is arbitrary and occurs at the discretion of the implementation.
Note that the thread which comes out of waiting because of the notify() method will not be able to proceed until the current thread relinquishes the lock on this object. The awakened thread just changes to the runnable state and it is ready to be scheduled again. The awakened thread will compete in the usual manner with any other threads that might be actively competing to synchronize on this object. The awakened thread enjoys no reliable privilege or disadvantage in being the next thread to lock this object.
When awakened thread(thread which has come out of waiting because of notify() method) gains control of the object, all its synchronization claims on the object are restored to the situation as of the time that the wait method was invoked that is on return from the wait method, the synchronization state of the object and of thread is exactly as it was when the wait method was invoked.

notifyAll() method in Java

Wakes up all the threads that called wait() on the same object. As explained in notify() any one of the threads will be granted access to the object.
Spurious wakeup
Once wait is called on an object the thread that is currently executing with in the synchronized context waits until notify() or notifyAll() method is called. But there is a possibility that a waiting thread resumes again even when notify() or notifyAll() are not called (this will rarely occur in practice). This is known as spurious wakeup in Java.
To guard against spurious wakeup the recommendation is that call to wait() method should be with in a loop that checks the condition on which the thread is waiting.
synchronized (obj)
{
  while (condition does not hold)
  obj.wait(timeout);
  ... // Perform action appropriate to condition
}

Producer-Consumer example using wait, notify

One of the oft mentioned example of producer and consumer implemented with two threads, where producer thread produces a number, puts it in a list and then consumer thread gets that number out of the list. Since a shared list is used between these two threads, so to make sure that both threads work in tandem wait/notify mechanism is used for inter-thread communication.

class Producer implements Runnable
{
List<Integer> sharedListObj;
Producer(List<Integer> sharedListObj)
{
this.sharedListObj = sharedListObj;
         }
public void run()
{
int i = 0;
while(true)
{
synchronized (sharedListObj)
{
// While condition as mandated to avoid spurious wakeup
while(sharedListObj.size() >= 1)
{
try
{
sharedListObj.wait();
}
catch (InterruptedException e)
{
e.printStackTrace();
}

}
// Putting value in the list
System.out.println("Adding to queue - "
                                        + Thread.currentThread().getName() + " " + ++i);
sharedListObj.add(i);
sharedListObj.notify(); 
// To get out of while(true) loop, putting
// only 5 values
if(i > 4) break;
}
}
}         
}
class Consumer implements Runnable
{
    List<Integer> sharedListObj;
    Consumer(List<Integer> sharedListObj)
{
        this.sharedListObj = sharedListObj;
    }
public void run() 
{   
while(true)
{
synchronized (sharedListObj) 
{
while(sharedListObj.size() < 1)
{
try 
{
sharedListObj.wait();
catch (InterruptedException e) 
{
e.printStackTrace();
}                   
}
// Getting value from the list
System.out.println("Getting from queue " 
                                      + Thread.currentThread().getName() + " " + sharedListObj.get(0));
// To get out of while(true) loop
if(sharedListObj.get(0) == 5) break;
sharedListObj.remove(0);
sharedListObj.notify();       
}
}
    }
}
public class InterThreadDemo 
{
    public static void main(String[] args) 
{
// This is the shared list shared between producer
        // and consumer
        List<Integer> sharedListObj = new ArrayList<Integer>();
        Thread t1 = new Thread(new Producer(sharedListObj), "Producer");
        Thread t2 = new Thread(new Consumer(sharedListObj), "Consumer");
        t1.start();
        t2.start();   
    }
}


Output:
Adding to queue - Producer 1
Getting from queue Consumer 1
Adding to queue - Producer 2
Getting from queue Consumer 2
Adding to queue - Producer 3
Getting from queue Consumer 3
Adding to queue - Producer 4
Getting from queue Consumer 4
Adding to queue - Producer 5
Getting from queue Consumer 5

Previous Post
Next Post

0 comments: