Ad

Saturday 28 September 2013

Producer and Consumer threads example for use of Synchronization and wait/notify/notifyAll in java multithreading

In this post,we will see about  importance of synchronization in java multithreading  and how wait,notify and notifyAll methods can be used in thread communication

Why we need to go for Synchronization?
    When more than one thread access the shared object,the state of the shared object won't be consistent across the thread.To fix this we have to make only one thread access/modify the shared object at any time , so that its state or value will be consistent across the threads that access it.How we can achieve this?The answer is Synchronization.If we make the method or any block as synchronized,the thread which first enters the method or block will get the object lock for  the object which is synchronized.If any other thread try to enter the synchronized block for the same object,it has to wait until the former thread releases the object lock of the same object.The thread  will releases the object lock either when it completes and leaves the synchronized block/method or the current executing thread call the wait method on the object which it was synchronized.

wait ,notify and notifyAll Method:
These methods are available in java.lang.Object and plays a  very vital role in communication between threads.
wait:
If we call the wait method,the current executing thread will release the object lock of the object(on which the wait method was called) and will go to sleeping state(will wait until it get any notification).
notify:
The notify method call on the object will make the thread which is waiting for the object lock for the object(on which notify was called) to resume its execution as soon as the object lock was avilable.
notifyAll:
notifyAll method will notify all the threads which are waiting for the object lock whereas notify will notify only a single thread.

The wait,notify and notifyAll method should be called only in synchronization context,since the thread can acquire the object lock only if it enters synchronization block or method.Lets understand this with a sample program- Producer and Consumer threads.we have two threads producer and consumer Thread which shares an object queue which is an list of items.The role of producer is to add the item to queue and to notify the consumer about the newly added item.The consumer will take the item from queue and further process it.These two threads should not access the shared object queue at the same time.


import java.util.ArrayList;
import java.util.List;

public class ThreadWaitNotifyExample {

public static void main(String[] args) {
List<String> queue= new ArrayList<String>();
/*Construct and start the Consumer Thread*/
ConsumerThread consumer= new ConsumerThread(queue);
Thread consumerThread=new Thread(consumer,"Consumer Thread");
consumerThread.start();
/*Construct and start the Producer Thread*/
ProducerThread producer= new ProducerThread(queue);
Thread producerThread=new Thread(producer,"Producer Thread");
producerThread.start();

}
}

/*Class For Producer Thread*/
class ProducerThread implements Runnable
{
private List<String> queue=null;
 
public ProducerThread(List<String> queue)
{
this.queue=queue;
}

@Override
public void run() {
int i=0;
String threadName=Thread.currentThread().getName();
while(true)
{
i++;
synchronized (queue) {
    System.out.println(threadName+":Got the queue Object lock");
System.out.println(threadName+":Adding the Item to Queue:Item"+i);
queue.add("Item"+i);
System.out.println(threadName+":Abt to Notify the Consumer for the new Item");
queue.notifyAll();
    System.out.println(threadName+":Releases the queue Object lock");
}
if(i==2)
break;
}
}

}

/*Class For Consumer Thread*/
class ConsumerThread implements Runnable
{
private List<String> queue=null;
public ConsumerThread(List<String> queue)
{
this.queue=queue;
}
@Override
public void run(){
String threadName=Thread.currentThread().getName();
synchronized (queue)
{
 while(true) {
if(queue.size()==0)
{
try {
System.out.println(threadName+":Going to Wait state,Since the Queue is empty");                              
      System.out.println(threadName+":Releases the queue Object lock");
queue.wait();
System.out.println(threadName+":resumed and got the queue object lock");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
String item=queue.remove(0);
System.out.println(threadName+":Got the Item from Queue:"+item);
}
}
}
}

synchronized (queue) - The thread which first enter this synchronized block will acquire the object lock of the queue object,so that any other thread tries to get the object lock of the queue(by entering the the synchronized block of the queue) has to wait until the former thread releases the same.Thus at any point of  time both producer and  consumer Thread will not access the queue object at the same time.The detailed program flow is described below:

  • The consumer thread first acquires the object lock of the queue by entering the synchronized block.
  • The producer thread cannot acquire the lock and hence cannot enter the synchronized block.
  • Since the initial queue size is 0,consumer thread goes to wait state by calling the wait method on the queue object and the object lock of the queue will be released.
  • The producer thread will now acquire the object lock by entering the synchronized block and add an item to the queue.Then it will notify all the threads that are waiting for the queue object lock by calling notifyAll on the queue object.
  • Once the producer thread leaves the Synchronized block(object lock will be released),the notified consumer thread will resume and get the added item from queue and process it.
  • Once the queue size becomes zero,consumer thread will again release queue object lock by calling the wait method on queue object.
  • As soon as queue object lock is released,the producer thread will acquire it(by entering the synchronized block) and add an item to queue and notifies the thread waiting for the queue object lock.So this process of adding and retrieval  continues.                                                                     
When you run the above program ,you will get the output as shown below:

Consumer Thread:Going to Wait state,Since the Queue is empty
Consumer Thread:Releases the queue Object lock
Producer Thread:Got the queue Object lock
Producer Thread:Adding the Item to Queue:Item1
Producer Thread:Abt to Notify the Consumer for the new Item
Producer Thread:Releases the queue Object lock
Consumer Thread:resumed and got the queue object lock
Consumer Thread:Got the Item from Queue:Item1
Consumer Thread:Going to Wait state,Since the Queue is empty
Consumer Thread:Releases the queue Object lock
Producer Thread:Got the queue Object lock
Producer Thread:Adding the Item to Queue:Item2
Producer Thread:Abt to Notify the Consumer for the new Item
Producer Thread:Releases the queue Object lock
Consumer Thread:resumed and got the queue object lock
Consumer Thread:Got the Item from Queue:Item2
Consumer Thread:Going to Wait state,Since the Queue is empty
Consumer Thread:Releases the queue Object lock

  

No comments:

Post a Comment