Article 365The Original Project, Chapter 11.


Today! Mr. Lighthouse told us:


JVM source code analysis of object. wait/notify implementation




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, among which wait and notify methods to achieve multi-threaded cooperation to provide a guarantee.
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 lockthread A get lockthread B is waiting to get lockthread A do wait methodthread B get lockthread B do notify methodwait endCopy the code


Premise: Call wait and notify from the same LOCK object. When thread A executes wait, the thread is suspended. 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, you can imagine a scenario where the LOCK object maintains a list of wait queues. Execute lock wait on thread A and save thread A to list; 3. Execute notify lock in thread B, and take thread A out of the wait queue to continue execution; Of course, Hotspot implementation is not that simple.


In the above code, there are several questions:
1. Why acquire synchronized locks before entering wait/notify?
2. If thread A acquires A synchronized lock and executes wait, thread B can acquire the lock again.


Why 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 }}Copy the code

The bytecode generated by javap from the synchronized code block contains the monitorenter ** and monitorexit ** directives.

Monitorenter fetches the object’s monitor, while lock.wait() is implemented by calling native wait(0).

The current thread must own this object’s monitor.

Indicates that a thread executing the lock.wait() method must hold the lock object’s monitor. If the wait method is executed in synchronized code, the thread obviously already holds the monitor.


Code execution process analysis

1. In A multi-core environment, thread A and thread B may simultaneously execute monitorenter and acquire the monitor associated with the Lock object. Only one thread can associate the monitor, assuming thread A succeeds in locking. 2. Thread B fails to lock and enters the waiting queue. 3. Thread A continues and what happens when the wait method is executed? Wait interface comment:

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 places the current thread in the wait set, waits to be awakened, and abandons all synchronization declarations on the lock object. This means that thread A releases the lock, and thread B can re-lock the lock. How does thread B know that thread A has released the lock? Very confused…

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

A notifyAll method wakes up all threads in the Wait set of Monitor.

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

After executing notify, thread B will not immediately wake up the waiting thread. If thread B sleeps for 5s after executing notify, thread A will have to wait until thread B still holds monitor.


When will the wait set thread be awakened?

To answer these questions, we need to look at the implementation of the JVM. This article uses HotSpot VIRTUAL Machine version 1.7 as an example


What is monitor?
In the HotSpot VIRTUAL machine, Monitor is implemented with ObjectMonitor.




Each thread has two ObjectMonitor object lists, free and Used. If the current free list is empty, the thread will request an ObjectMonitor assignment to the global Global List.
ObjectMonitor has two queues: _WaitSet and _EntryList, which hold the list of ObjectWaiter objects. _owner points to the thread that gets the ObjectMonitor object.



**_WaitSet ** : Threads in wait state will be added to the wait set;


_EntryList: the thread in the lock waiting block state is added to the entry set.


ObjectWaiter

The ObjectWaiter object is a bidirectional linked list that stores data such as _thread (current thread) and current state (TState). Each thread waiting for a lock is encapsulated as an ObjectWaiter object.

Wait method implementation

The lock.wait() method finally passes the void wait(jLong Millis, bool Interruptable, TRAPS) of ObjectMonitor; Implementation: 1. Encapsulate the current thread into ObjectWaiter object node;



2, through the
ObjectMonitor::AddWaiterMethod to add node to the _WaitSet list;



3. Release the current ObjectMonitor object with the ObjectMonitor::exit method, so that other competing threads can obtain the ObjectMonitor object.


4. Eventually the underlying Park method suspends the thread;


Implementation of notify method

The lock.notify() method is finally implemented using void notify(TRAPS) of ObjectMonitor:

1. If the current _WaitSet is empty, that is, there are no waiting threads, then return;

2, through ObjectMonitor: : DequeueWaiter method, obtain the _WaitSet list first ObjectWaiter nodes, implementation is simple.

Note that the notify annotation in the JDK randomly wakes up a thread, which is actually the first ObjectWaiter node

3. Add the removed ObjectWaiter node to _EntryList or pass, depending on the strategy

The Atomic:: Cmpxchg_ptr directive spins CXQ. The cMPxchg_ptr directive spins CXQ. The cMPxchg_ptr directive spins CXQ.


NotifyAll method implementation

The lock.notifyall () method is finally implemented with void notifyAll(TRAPS) of ObjectMonitor:

Fetch the ObjectWaiter node of _WaitSet through the for loop and add it to _EntryList or spin it, depending on the strategy.

From the JVM’s method implementation, you can see: Notify and notifyAll do not release the ObjectMonitor they hold. In fact, the ObjectMonitor is actually released during monitoreXit. Once the ObjectMonitor is released, The threads held by the ObjectWaiter node in the Entry set can then begin to compete with ObjectMonitor objects for locking.

[Information] [interview] [resume] I have prepared the interview materials and resume template of the first-line big factory