How to use wait of notify of and notifyAll of in Java

  • 2020-04-01 02:14:50
  • OfStack

Wait (),notify(), and notifyAll() are methods for java.lang.object:
Wait () : Causes the current thread to wait until another thread invokes the notify () method or the notifyAll () method for this object.
Notify () : Wakes up a single thread that is waiting on this object "s monitor.
NotifyAll () : Wakes up all threads that are waiting on this object "s monitor.
These three methods are the underlying mechanisms for Blocking between threads and controlling inter-process communication provided by the Java language. Before explaining how to use it, here are two points:
1. Just as any object in Java can be a Lock, any object can be a Condition queue. The wait(), notify(), and notifyAll() in this object are intrinsic methods to the conditional queue.
2. The inherent lock of an object is related to its inherent condition queue. In order to invoke the method of the condition queue in object X, you must acquire the lock of object X. This is because the mechanism for waiting for state conditions and the mechanism for ensuring continuity of states are tightly coupled.
(An object's intrinsic lock and its intrinsic condition queue are related: In order to call any of the condition queue methods on object X, You must hold the lock on x. This is because the mechanism for waiting for the state - -based the conditions is necessarily tightly bound to the mechanism fo preserving state consistency)
According to the above two points, when a wait(), notify(), or notifyAll() is called, the lock must be acquired first, the state variable must be protected by the lock, and the native lock object is the same object as the native condition queue object. That is, to perform a wait or notify on an object, the object must be locked, and the corresponding state variable is protected by the object lock.
Now that we know how to use it, let's ask the following questions:
1. What happens if you don't get a lock when you perform wait, notify?
See the code:

public static void main(String[] args) throws InterruptedException {
        Object obj = new Object();
        obj.wait();
        obj.notifyAll();
}

To perform the above code, throws Java. Lang. Exception IllegalMonitorStateException.
2. What happens if you do a wait, notify and don't get a lock on the object?
See the code:

public static void main(String[] args) throws InterruptedException {
        Object obj = new Object();
        Object lock = new Object();
        synchronized (lock) {
            obj.wait();
            obj.notifyAll();
        }
    }

To execute the code also sell Java. Lang. Exception IllegalMonitorStateException.
3. Why is it necessary to acquire a lock for this object when performing a wait, notify?
This is because wait and notify can have Race conditions if there are no locks. Consider the following producer and consumer scenarios:
1.1 producer check condition (if the cache is full) 1.2 producers must wait
2.1 consumer consumes a unit of cache -> 2.2 reset the condition (if the cache is not full) -> 2.3 invoke notifyAll() to wake up the producer
The order we want is: 1.1-> 1.2 - > 2.1 - > 2.2 - > 2.3
But in the case of multi-threading, the order might be 1.1-> 2.1 - > 2.2 - > 2.3 - > 1.2. In other words, the consumer notifies all before the producer waits, and the producer waits forever.
So, to solve this problem, you must acquire the lock of the object during wait and notifyAll to ensure synchronization.
See the following simple model of a producer, a consumer, and a unit cache with wait, notify:

public class QueueBuffer {
    int n;
    boolean valueSet = false;
    synchronized int get() {
        if (!valueSet)
            try {
                wait();
            } catch (InterruptedException e) {
                System.out.println("InterruptedException caught");
            }
        System.out.println("Got: " + n);
        valueSet = false;
        notify();
        return n;
    }
    synchronized void put(int n) {
        if (valueSet)
            try {
                wait();
            } catch (InterruptedException e) {
                System.out.println("InterruptedException caught");
            }
        this.n = n;
        valueSet = true;
        System.out.println("Put: " + n);
        notify();
    }
}


public class Producer implements Runnable {
    private QueueBuffer q;
    Producer(QueueBuffer q) {
        this.q = q;
        new Thread(this, "Producer").start();
    }
    public void run() {
        int i = 0;
        while (true) {
            q.put(i++);
        }
    }
}


public class Consumer implements Runnable {
    private QueueBuffer q;
    Consumer(QueueBuffer q) {
        this.q = q;
        new Thread(this, "Consumer").start();
    }
    public void run() {
        while (true) {
            q.get();
        }
    }
}


public class Main {
    public static void main(String[] args) {
        QueueBuffer q = new QueueBuffer(); 
        new Producer(q); 
        new Consumer(q); 
        System.out.println("Press Control-C to stop."); 
    }
}

So, the JVM by thrown when performing exception IllegalMonitorStateException, to ensure that wait, notify, got a lock of the object, thus eliminating hidden Race Condition.
Finally, write a multithreaded program that alternately outputs 1,2,1,2,1,2...
Use wait, notify to solve:

public class OutputThread implements Runnable {
    private int num;
    private Object lock;
    public OutputThread(int num, Object lock) {
        super();
        this.num = num;
        this.lock = lock;
    }
    public void run() {
        try {
            while(true){
                synchronized(lock){
                    lock.notifyAll();
                    lock.wait();
                    System.out.println(num);
                }
            }
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    public static void main(String[] args){
        final Object lock = new Object();
        Thread thread1 = new Thread(new OutputThread(1,lock));
        Thread thread2 = new Thread(new OutputThread(2, lock));
        thread1.start();
        thread2.start();
    }
}

Related articles: