The Object class has three methods, wait(), notify(), and notifyAll() that help threads communicate about the status of an event that the threads care about. wait(), notify(), and notifyAll() must be called from within a synchronized context! A thread can`t invoke a wait or notify method on an object unless it owns that object`s lock.

Consider the following scenario where thread interaction is must-:

Think of a computer-controlled machine that cuts pieces of fabric into different shapes and an application that allows users to specify the shape to cut. The current version of the application has one thread, which loops, first asking the user for instructions, and then directs the hardware to cut the requested shape:
PROBLEM WITH SINGLE THREAD-:This design is not optimal because the user can`t do anything while the machine is busy and while there are other shapes to define. We need to improve the situation.

SOLUTION
-
A simple solution is to separate the processes into two different threads, one of them interacting with the user and another managing the hardware. The user thread sends the instructions to the hardware thread and then goes back to interacting with the user immediately. The hardware thread receives the instructions from the user thread and starts directing the machine immediately. Both threads use a common object to communicate, which holds the current design being processed.

Demo for the Thread Interaction using synchronization-

class Stack {

int contents=0;
boolean available = false;
        
synchronized int pop() {
    if (available==false)
    try {
        wait();
     }
    catch (InterruptedException e) {
        System.out.println("InterruptedException caught");
     }
 System.out.println("consume: " + contents);
 available=true;
 notifyAll();
 return contents;
}

synchronized void push(int num) {
    if (available==true) {
    try {
        wait();
        }
    catch (InterruptedException e) {
       System.out.println("InterruptedException caught");
    }
    contents=num;
    System.out.println("Produce: " + contents);
    available= false;
    System.out.println("hi " );
              
    notifyAll();
  }
}

class Producer extends Thread {
    Stack s;
    
    Producer(Stack s) {
        this.s=s;
        this.start();
    }

    public void run() {
        int i=0;
        while(true)
        {
            s.push(++i);
        }
   }
}

class Consumer extends Thread{
    Stack s;
    
    Consumer(Stack s) {
        this.s=s;
        this.start();
    }

    public void run() {
        while(true)
        {
           s.pop();
        }
   }
}

public class StackSync{
    public static void main(String[] args)
    {
        Stack s=new Stack();
        new Producer(s);
        new Consumer(s);
    }
}