Friday, January 21, 2011

Wait() and Notify() in Java

Dear Reader, 
This is a quite confusing topic. However I hope my explanations will clear your doubt.
I am writing here wait() and notify() method use case. This is also called producer-consumer
problem. Just to remind: you can replace notify() with notifyAll() in the given examples below.
The only difference is notify() awakens only one thread which is waiting to get lock on an object.
notifyAll() awakens all threads who are waiting to get hold of that object. However it is always CPU's 
task to which thread CPU allows first to get hold of object. More discussions with example below:

In my example, I have a Bucket Object and two methods on it "InsertIntoBucket" and "DeleteFromBucket".
I have put a Bucket size, so that it shouldn't grow unlimited. Also one more variable is required 
for checking whether Bucket is full or not. If full, don't insert data. If empty, don't delete data.

Wanted behavior: If bucket is full, don't insert data. If bucket is empty, don't delete the data.
Unwanted behavior: Even If bucket is full, try to insert data. Even If bucket is empty, try to delete data. And cause Exceptions..

Points to remember, read carefully:

1) Wait(), Notify() and NotifyAll() are methods of Object class and it is the father of all APIs in Java so 
   these methods are accessible to Thread class objects too.

2) Each object has its own lock means When multiple threads are trying to access only one object, the Variables 
   of Object may result in unwanted states causing wrong output to the caller threads. Remember Threads are also 
   objects but here I am not talking about those Thread objects. 
   Assume same bucket is trying to get filled and vacated by data by two different Threads. So Bucket is the common 
   resource being shared by multiple threads.

3) If we allow these 2 threads to access these two methods, they will access randomly as it is decided by the 
   CPU and we don't have control on the Scheduling, Causing unwanted behaviour.

4) If we make these two methods as Synchronized, only one thread will enter each method (for sake, two threads are 
   trying to access two methods, you can add as many threads you want to access these two methods). Now even if Bucket 
   is full, DataInserter thread will try to insert data and if Bucket is empty, DataDeleter thread will try to delete 
   the data as both have entered into separate blocks, causing Unwanted behavior. Means no thread to going to wait, 
   till other thread does the task: Means:

    a) Bucket is full, DataInserter thread is not waiting, so that DataDeleter will vacate the bucket for allowing 
    other thread to insert data successfully.
    b) Bucket is empty, DataDeleter thread is not waiting, so that DataInserter will insert some data for allowing 
    other thread to delete data successfully.
   
5) Wait() and Notify() can only be within Synchronized and non static methods. Many times interviewers ask what if
   write just wait() or notify() in main method. Please note: Compiler will not compile your code as wait() and 
   notify() are non-static methods and you can't access them in main method directly.

6) After implementing this tool, DataDeleter thread will wait for getting some data into Bucket by DataInserter thread.
Once DataInserter thread inserts the data, it will call Notify(), so that waiting thread will come to know the data
is available now in bucket. Same thing for other thread too.

7) Wait() means, current thread (which is running inside that block, where Wait() is defined) will leave the object(Bucket) 
   lock, but it is still there in that block. Once Notify() is called from other thread, this thread will come into action 
   and will start executing from that line of code.

8) Common point: Notify() will wake up only one waiting thread (which thread, chosen by CPU), NotifyAll() will wake-up all 
   waiting threads. Check at the beginning of this article.


Now Coding Implementation (It is quite easier):
//Please note, there are 4 java files, all are in same package (default):
    BucketDeleter.java (Thread File)
    BucketInserter.java (Thread File)
    TestWaitNotify.java (Main Java File to start/run this tool).
    WaitNotify.java (Object with methods and variables)

==========================TestWaitNotify.java===================
public class TestWaitNotify {
    public static void main(String[] args) {
     
        WaitNotify commonObject = new WaitNotify();
        //new BucketDeleter(commonObject);
        new BucketDeleter(commonObject);
        
        new BucketInserter(commonObject);
        //new BucketInserter(commonObject);
        //new BucketInserter(commonObject);
        
        //Un-comment the above thread creation code to see better output, as if 
        //more number of threads are created then result can be seen better.
        System.out.println("Running in infinite loop, Press Control-C to stop."); 
    }
}

===========================WaitNotify.java====================
import java.util.*;
public class WaitNotify {
    int number;

    public List bucket = null;
    public WaitNotify(){
        bucket = new ArrayList();
    }

    public synchronized void insertIntoBucket(int tempNumber) {
        if(bucket.size()<10) {
            bucket.add(tempNumber);
            System.out.println("Number getting Inserted: "+tempNumber+", Size after insertion: "+bucket.size());
        }
        else {
            System.out.println("List is full, no more insertion");
            try {
                System.out.println("Waiting now in insertion method...");
                wait();  
            }
            catch(Exception e) {
            }
        }
        notify();  //Never put this method in above else block else it will cause deadlock after full insertion.
    }

    public synchronized void deleteFromBucket() {
        if(bucket.isEmpty()) {
            System.out.println("Bucket is empty, can't delete the data");
            try {
                System.out.println("Waiting now in Deletion method...");
                wait();
            }
            catch(Exception e) {
            }
        }
        else {
            Object obj = bucket.remove(bucket.size()-1);
            System.out.println("Deleting one number from bucket: "+ obj+", Size after deletion: "+bucket.size());
            notify();
        }
    }
}

========================BucketInserter.java====================
public class BucketInserter implements Runnable {
    WaitNotify commonObject;
    BucketInserter(WaitNotify commonObject) {
        this.commonObject = commonObject;
        new Thread(this, "BucketInserter").start();
    }
    public void run() {
        int i = 0;
        while(true) {
         try {
    Thread.sleep(1000); //You can comment this, added just to see output properly.
   } catch (InterruptedException e) {
    e.printStackTrace();
   }
   i=i+1;
            commonObject.insertIntoBucket(i);
        }
    }
}

=========================BucketDeleter.java=====================
public class BucketDeleter implements Runnable {
    WaitNotify commonObject;
    BucketDeleter(WaitNotify commonObject) {
        this.commonObject = commonObject;
        new Thread(this, "BucketDeleter").start();
    }
    public void run() {
        while(true) {
         try {
    Thread.sleep(1000); //You can comment this, added just to see output properly.
   } catch (InterruptedException e) {
    e.printStackTrace();
   }
            commonObject.deleteFromBucket();
        }
    }
}
====================
//Now Run the main Class (TestWaitNotify.java) and analyze the output.
//Run the Main Class after commenting Wait() and Notify() methods.
//Run the Main Class after removing Synchronized keyword and Wait() and Notify().

================Another sample example of wait() method I am writing:
//wait() method throws InterruptedException which is a CheckedException, so must 
//be declared else code will not compile.

public class MainWait {
 public static void main(String[] args) throws InterruptedException {
  new MainWait().waitForMe();   
 }
 //public synchronized void waitForMe() throws InterruptedException{
 public void waitForMe() throws InterruptedException{
  wait();  //Unhandled Exception InterruptedException
 }
}
//wait() must be called inside a synchronized method or block, so above code gives output:=====
Exception in thread "main" java.lang.IllegalMonitorStateException: current thread not owner
 at java.lang.Object.wait(Native Method)
 at java.lang.Object.wait(Object.java:474)
 at MainWait.waitForMe(MainWait.java:7)
 at MainWait.main(MainWait.java:3)

But if you make the method synchronized as :
   public synchronized void waitForMe() throws InterruptedException

or put wait() inside synchronized(this) like below:
   public void waitForMe() throws InterruptedException{
      synchronized(this){
         wait();  //Unhandled Exception InterruptedException
      }
   }

//Then your code will run fine and output will be waiting....You have to close the application, I mean click the RED Square button on MyEclipse....

Also deadlock can be seen here: http://deepakmodi2006.blogspot.in/2011/12/deadlock-in-java.html

-------------------------------END---------------------------------

1 comment:

  1. Just to add checking a boolean variable periodically is called "busy waiting" technique and its not recommended. wait/notify is best way for inter thread communication but keep in mind if you have more than two threads its always better to use notifyAll(), Its also good to know How to find if a thread holds lock on a particular object in Java .

    On a related note incorrect written multi threaded programs often results in deadlock specially if number of shared resources are more and number of accessing threads are also high its worth knowing How to find and fix deadlock in Java ?

    Thanks
    Javin

    ReplyDelete