Object.wait/notify implementation of JVM source code analysis

Original: https://www.jianshu.com/p/f4454164c017


Jianshu Zhan
Xiaolang Please indicate the original source for reprinting, thank you!

The simplest things often contain the most complex implementations, because it is necessary to provide a stable foundation for the existence of the upper layer. Object is the base class of all objects in java, and its value is self-evident. The wait and notify methods are Implementing multi-threaded cooperation provides guarantees.

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

        new Thread(new Runnable() {
            @Override
            public void run() {
                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() {
                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();
    }
}
Results of the:

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

Premise : The wait and notify methods are called by the same lock object.
1. When thread A executes the wait method, the thread will be suspended;
2. When thread B executes the notify method, it will wake up a suspended thread A;

What is the relationship between the lock object, thread A and thread B? According to the above conclusions, a scenario can be imagined:
1. The lock object maintains a waiting queue list;
2. Thread A executes the lock's wait method and saves thread A to the list;
3. Thread B executes the lock's notify method , remove thread A from the waiting queue and continue to execute; of
course, the implementation of Hotspot cannot be so simple.

In the above code, there are several questions:

1. Why acquire the synchronized lock before entering the wait/notify method?
2. Thread A acquires the synchronized lock, executes the wait method and suspends, how does thread B acquire the lock again?

Why use synchronized?
static void Sort(int [] array) {
    // synchronize this operation so that some other thread can't
    // manipulate the array while we are sorting it. This assumes that other
    // threads also synchronize their accesses to the array.
    synchronized(array) {
        // now sort elements in array
    }
}

**monitorenter** and **monitorexit** instructions are included in the bytecode generated by the synchronized code block through javap.


The monitor of the object can be obtained by executing the monitorenter instruction, and the lock.wait() method is implemented by calling the native method wait(0). There is such a sentence in the interface comment:

The current thread must own this object's monitor.

It means that when the thread executes the lock.wait() method, it must hold the monitor of the lock object. If the wait method is executed in the synchronized code, the thread obviously already holds the monitor.

Code execution process analysis

1. In a multi-core environment, threads A and B may execute the monitorenter instruction at the same time and obtain the monitor associated with the lock object. Only one thread can be associated with the monitor, assuming that thread A executes the lock successfully;
2. Thread B competes for the lock If it fails, enter the waiting queue to wait;
3. Thread A continues to execute. When the wait method is executed, what will happen? wait interface annotation:

This method causes the current thread to place itself in the wait set for this object and then to relinquish any and all synchronization claims on this object.

The wait method will put the current thread into the wait set, wait to be woken up, and give up all synchronization declarations on the lock object, which means that thread A releases the lock, and thread B can re-execute the locking operation, but there is another question: in the thread A's wait method releases the lock, and thread B acquires the lock. What happened during this period? How does thread B know that thread A has released the lock? so confused....

4. Thread B performs the locking operation successfully. For the notify method, JDK notes: the notify method will select any thread in the wait set to wake up;

Wakes up a single thread that is waiting on this object's monitor. If any threads are waiting on this object, one of them is chosen to be awakened. The choice is arbitrary and occurs at the discretion of the implementation

Note on the notifyAll method: The notifyAll method will wake up all threads in the monitor's wait set.

Wakes up all threads that are waiting on this object's monitor.

5. After executing the notify method, it will not wake up the waiting thread immediately. Add a sleep code after the notify method to see the effect. If thread B sleeps for 5s after executing the notify method, during this time, thread B still holds With a monitor, thread A can only continue to wait;

So when will the thread of the wait set be woken up?

To answer these questions, it is necessary to analyze the relevant implementation of jvm. This article takes the version 1.7 of the HotSpot virtual machine as an example.

What is monitor?

In the HotSpot virtual machine, the monitor is implemented by ObjectMonitor.


Each thread has two lists of ObjectMonitor objects, namely free and used lists. If the current free list is empty, the thread will request to allocate ObjectMonitor to the global global list.

There are two queues in the ObjectMonitor object: _WaitSet and _EntryList, which are used to save the list of ObjectWaiter objects; _owner points to the thread that obtained the ObjectMonitor object.


**_WaitSet ** : Threads in wait state will be added to wait set;
_EntryList : Threads in wait state of lock block will be added to entry set;

ObjectWaiter

The ObjectWaiter object is a doubly linked list structure that stores data such as _thread (current thread) and the current state TState. Each thread waiting for a lock will be encapsulated into an ObjectWaiter object.

Implementation of wait method

lock.wait()The method is finally implemented by ObjectMonitor void wait(jlong millis, bool interruptable, TRAPS);:
1. Encapsulate the current thread into an ObjectWaiter object node;

2. ObjectMonitor::AddWaiterAdd node to _WaitSet list by method;

3. Release the current ObjectMonitor object through the ObjectMonitor::exitmethod, so that other competing threads can obtain the ObjectMonitor object.

4. Finally, the underlying park method will suspend the thread;

notify method implementation

lock.notify()The method is finally implemented by ObjectMonitor void notify(TRAPS):
1. If the current _WaitSet is empty, that is, there is no waiting thread, it will return directly;
2. Through the ObjectMonitor::DequeueWaitermethod, get the first ObjectWaiter node in the _WaitSet list, the implementation is also very simple.
It should be noted here that the annotation of the notify method in jdk is to randomly wake up a thread, which is actually the first ObjectWaiter node


3. According to different strategies, add the extracted ObjectWaiter node to _EntryList or perform spin operation cxq through the Atomic::cmpxchg_ptr instruction . The specific code implementation is a bit long, so I won't post it here. Interested students can see objectMonitor::notify method;

notifyAll method implementation

lock.notifyAll()The method is finally implemented by ObjectMonitor void notifyAll(TRAPS):
take out the ObjectWaiter node of _WaitSet through a for loop, and add it to _EntryList or perform a spin operation according to different strategies.

From the method implementation of the JVM, it can be found that notify and notifyAll do not release the occupied ObjectMonitor object. In fact, the time point when the ObjectMonitor object is actually released is when the monitorexit instruction is executed. Once the ObjectMonitor object is released, the ObjectWaiter node in the entry set saves The threads can start to compete for the ObjectMonitor object for locking operations.



Author: Zhan Xiaolang
Link : https://www.jianshu.com/p/f4454164c017
Source: Jianshu The
copyright belongs to the author. For commercial reprints, please contact the author for authorization, and for non-commercial reprints, please indicate the source.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325994674&siteId=291194637