• Two Threads can communicate with each other by using wait(), notify() and notifyAll() methods. The Thread which is expecting updation is responsible to call wait() method then immediately the Thread will enter into waiting state.
  • The Thread which is responsible to perform updation, after performing updation it is responsible to call notify() method then waiting Thread will get that notification on the continue its execution with those updated items.
  • wait(), notify(), notifyAll() present in Object class but not in Thread class because Thread can call these methods on any java object.
    • To can wait(), notify(), notifyAll() methods on any objects, Thread should be owner of that object i.e., the Thread should has lock of that object i.e., the Thread should be in synchronized area. Hence, we can call wait(), notify(), notifyAll(0 methods only from synchronized area. Otherwise, we will get runtime exception saying IllegalMonitorStateException.
  • If a Thread calls wait() method on any object it immediately releases the lock of that particular object and entered into waiting state.
  • If a Thread calls notify() method on any object it releases the lock of that object but may not immediately.
  • Except wait(), notify(), notifyAll() there is no other method where Thread releases the lock.
method Is Thread releases the lock?
yield() NO
join() NO
sleep() NO
wait() YES
notify() YES
notifyAll() YES

which of the following are valid and not-valid?
[1]. If a Thread calls wait() method immediately it will enter into waiting state without releasing any lock.
[2]. If a Thread calls wait() method it releases the lock of that object but may not immediately.
[3]. If a Thread calls wait() method on any object, it releases all locks acquired by the Threads and immediately entered into waiting state.
[4]. If a Thread calls wait() method on any object it immediately releases the lock of that particular object and entered into waiting state.
[5]. If a Thread calls notify() method on any object it immediately releases the lock of that particular object.
[6]. If a Thread calls notify() method on any object it releases the lock of that object but may not immediately.

public final void wait() throws InterruptedException
public final native void wait(long ms) throws InterruptedException
public final void wait(long ms, int ns) throws InterruptedException
public final native void notify()
public final native void notifyAll()

Every wait() method throws InterruptedException which is checked exception. Hence, whenever we are using wait() method compulsory we should handle this InterruptedException either by try-catch or throws keyword. Otherwise, we will get compile time error.


ThreadLifeCycle
class ThreadA{
    public static void main(String[] argsthrows InterruptedException{
        ThreadB b = new ThreadB();
        b.start();
        synchronized(b){
            System.out.println("main Thread trying to call wait() method");
            b.wait();
            System.out.println("main Thread gets notification");
            System.out.println(b.total);
        }
    }
}
class ThreadB extends Thread{
    int total = 0;
    public void run(){
        synchronized(this){
            System.out.println("Child Thread starts calculating");
            for(int i = 0; i <= 100; i++){
                 total = total + i;
            }
            System.out.println("Child Thread giving notification call");
            this.notify();
        }
    }
}

Output:
[main Thread in sleep. So, Child Thread will execute]
Child Thread starts calculating
Child Thread giving notification call
main Thread trying to call wait() method
[still main Thread in waiting state because no one gives notify() method to main Thread]


Below program is to overcome this issue

class ThreadA{
    public static void main(String[] argsthrows InterruptedException{
        ThreadB b = new ThreadB();
        b.start();
        Thread.sleep(10000);
        synchronized(b){
            System.out.println("main Thread trying to call wait() method");
            b.wait(1000); // wait() method with milliseconds argument
            System.out.println("main Thread gets notification");
            System.out.println(b.total);
        }
    }
}
class ThreadB extends Thread{
    int total = 0;
    public void run(){
        synchronized(this){
            System.out.println("Child Thread starts calculating");
            for(int i = 0; i <= 100; i++){
                 total = total + i;
            }
            System.out.println("Child Thread giving notification call");
            this.notify();
        }
    }
}

Output:
Child Thread starts calculating
Child Thread giving notification call
main Thread trying to call wait() method
main Thread gets notification
5050

Producer-Consumer Problem:

  • ProducerThread is responsible to produce items to the Queue and the ConsumerThread is responsible to consume items from the Queue.
  • If Queue is empty then ConsumerThread will call wait() method and entered into waiting state.
  • After producing items to the Queue ProducerThread is responsible to call notify() method then waiting consumer will get that notification and continue its execution with updated items.
Producer-Cosumer

Difference between notify() and notifyAll()?

we can use notify() method to give the notification for only one waiting Thread. If multiple Threads are waiting then only in Thread is notified and the remaining Threads have to wait for further notifications.


Which Thread will be notified?

we can’t expect, it depends on JVM.


we can use notifyAll() to give the notification for all waiting Threads of a particular object.
Even though multiple Threads are notified but execution will be performed one by one because Threads required lock and only one lock is available.

Note: On which object we are calling wait() method, Thread required lock of that particular object.
For example, if we are calling wait() method on s1 then we have to get lock of s1 object but not s2 object.

Stack

Deadlock:

If two Threads are waiting for each other forever, such type of infinite waiting is called Deadlock.
synchronized keyword is the only reason for deadlock situation. Hence, while using synchronized keyword we have to take special care.
There are no resolution techniques for deadlock but several prevention techniques are available.

class A{
    public synchronized void d1(B b){
        System.out.println("Thread1 starts execution of d1() method");
        try{
            Thread.sleep(3000);
        }
        catch(InterruptedException e){
        }
        System.out.println("Thread1 trying to call B's last() method");
        b.last();
    }
    public synchronized void last(){
        System.out.println("Inside A, this is last() method");
    }
}
class B{
    public synchronized void d2(A a){
        System.out.println("Thread2 starts execution of d2() method");
        try{
            Thread.sleep(3000);
        }
        catch(InterruptedException e){
        }
        System.out.println("Thread2 trying to call A's last() method");
        a.last();
    }
    public synchronized void last(){
        System.out.println("Inside A, this is last() method");
    }
}
class DeadLock extends Thread{
    A a = new A();
    B b = new B();
    public void m1(){
        this.start();
        a.d1(b); //This line executed by main Thread
    }
    public void run(){
        b.d2(a);//This line executed by child Thread
    }
    public static void main(String[] args){
        DeadLock d = new DeadLock();
        d.m1();
    }
}

Output:
Thread1 starts execution of d1() method
Thread2 starts execution of d2() method
Thread1 trying to call B's last() method
Thread2 trying to call A's last() method

In the above program if we remove at least one synchronized keyword then the won’t enter into deadlock. hence synchronized keyword is the only reason for deadlock situation. Due to this, by using synchronized keyword we have to take special care.


Deadlock vs Starvation:

  • Long waiting of a Thread where, waiting never ends is called Deadlock.
  • whereas long waiting of a Thread where waiting ends at certain point is called Starvation.
  • For example, low priority Thread has to wait until completing all high priority Threads. It may be long waiting but ends at certain point which is nothing but starvation.