0 foreword

In the last section, the principle and optimization analysis of Synchronized keywords were described, while wait&notify, the other two keywords used with Synchronized, are the focus of this chapter. The simplest things often contain the most complex implementation, because it needs to provide a stable foundation for the existence of the upper layer, Object as the base class of all objects in Java, its existence value is self-evident, wait&notify method realization of multi-threaded cooperation to provide a guarantee.

1 source

Today we are going to look at two methods, wait&notify, in the Object class. There are five wait&notify methods, and there are only 12 wait&notify methods. Let’s start with the code in the JDK:

public final native void notify(a);

public final native void notifyAll(a);

public final void wait(a) throws InterruptedException {
    wait(0);
}

public final native void wait(long timeout) throws InterruptedException;

public final void wait(long timeout, int nanos) throws InterruptedException {
    if (timeout < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }

    if (nanos < 0 || nanos > 999999) {
        throw new IllegalArgumentException(
                            "nanosecond timeout value out of range");
    }
    // The processing of nanoseconds is not precise, just add 1 millisecond,
    if (nanos > 0) {
        timeout++;
    }

    wait(timeout);
}
Copy the code

Those are the five ways. Three of these methods are native, meaning they are executed by the VIRTUAL machine’s native C code. Two wait overloading methods ended up calling wait (long) anyway.

  1. Wait method: Wait is to release the object lock and enter the wait pool. To release an object lock, you must acquire the lock first. So wait must be written in a synchronized block, or an exception will be reported.

  2. Notify: also in the synchronized block, the two methods that call the object also need to acquire the lock of the object. Notify, notifyAll: wakes up the thread waiting for the synchronization lock on the object. Notify Wakes up an object to wait for a thread in the pool and places the thread in the lock pool of the object. Threads in the lock pool of an object can compete for the object lock and then execute it.

    1. If the thread is invoked by notify, the thread that enters wait first will be invoked first.
    2. If the thread is invoked by nootifyAll, the default is that the last thread to enter is invoked first, which is LIFO’s policy.

    Another point is that calls to notify and notifyAll do not release object locks. For example:

    public void test(a)
    {
        Object object = new Object();
        synchronized (object){
            object.notifyAll();
            while (true) {}}}Copy the code

    NotifyAll is called, but then an infinite loop is entered. As a result, the critical section cannot be released and the object lock cannot be released. So, even if it wakes up all the threads in the wait pool and puts them into the lock pool of the object, none of the threads in the lock pool will run because they can’t get the lock.

2 the usage

A simple example:

public class WaitNotifyCase {
    public static void main(String[] args) {
        final Object lock = new Object();

        new Thread(new Runnable() {
            @Override
            public void run(a) {
                System.out.println("thread A is waiting to get lock");
                synchronized (lock) {
                    try {
                        System.out.println("thread A get lock");
                        TimeUnit.SECONDS.sleep(1);
                        System.out.println("thread A do wait method");
                        lock.wait();
                        System.out.println("wait end");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run(a) {
                System.out.println("thread B is waiting to get lock");
                synchronized (lock) {
                    System.out.println("thread B get lock");
                    try {
                        TimeUnit.SECONDS.sleep(5);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    lock.notify();
                    System.out.println("thread B do notify method"); } } }).start(); }}Copy the code

Execution Result:

thread A is waiting to get lock
thread A get lock
thread B is waiting to get lock
thread A do wait method
thread B get lock
thread B do notify method
wait end
Copy the code

Prerequisite: The wait and notify methods must be called from the same LOCK object

  1. When thread A executes wait, the thread is suspended.
  2. When thread B executes notify, A suspended thread A is awakened.

What is the relationship between the lock object, thread A, and thread B? Based on the above conclusion, imagine a scenario:

  1. The Lock object maintains a list of wait queues;
  2. Execute lock wait on thread A and save thread A to list;
  3. The notify method of lock is executed in thread B, and thread A is removed from the wait queue to continue execution.