In a nutshell, the sleep method is a static method for Thread to sleep and release the CPU without releasing the lock. The wait method releases the lock while the current thread waits, which is the Object method. Also note that inline classes introduced after Java 14 do not have wait methods

Sleep () principle

public static native void sleep(long millis) throws InterruptedException;
Copy the code

Sleep () is a static method and native implementation of Thread. Call the underlying sleep function:

void THREAD_sleep(int seconds) {
#ifdef windows
    Sleep(1000L * seconds);
#else
    sleep(seconds);
#endif
}
Copy the code

Sleep: man7.org/linux/man-p…

wait(), notify(), notifyAll()

These apis are native implementations of the basic Java multithreaded synchronization class:

public final native void wait(long timeout) throws InterruptedException;
public final native void notify();
public final native void notifyAll();
Copy the code

So what about the underlying implementation? First, we need to clarify the basic mechanism for implementing shared memory locks in the JDK. Each Object has an ObjectMonitor that contains three special data structures: CXQ (which is actually the Main Contention List), EntryList, and WaitSet; A thread can only appear in one of them at a time. Let’s start with CXQ:A thread that attempts to acquire an Object lock will enter CXQ if its first attempt (i.e., trying to update the CAS lightweight lock) fails. The method of entry is that CAS updates the CXQ pointer to itself, and if successful, its next points to the remaining queue. CXQ is a LIFO queue, which is designed to:

  1. After entering the CXQ queue, each line enters the spin state for a period of time and tries to obtain the lock. If the lock fails, the line enters the Park state. The meaning of this spin is that if the lock is held for a very short time, switching between user and system states will affect lock performance if the application goes directly to the Park state. This spin reduces switching;
  2. If the CAS is at the top of the queue, then the operation time is shorter. However, if this mechanism is used entirely, then of course the CAS will update the queue headers very frequently. So EntryList was introduced to reduce contention:If the EntryList is null and the CXQ is not null, A Thread is pulled from the end of the CXQ and placed into the EntryList. If one of the EntryList threads is marked as the head node, the EntryList may be larger than one, and the succeeded thread will then go into spin to try to get the lock (note that, after the first spin, the precursor is succeeded, The thread remains in park state after that. If it succeeds, it becomes the owner; otherwise, it returns to EntryList. Michael Scott’s “2Q” algorithm is used to reduce contention between two queues. If a thread becomes owner and executes the wait method, it enters WaitSet:Object.wait() low-level implementation

Void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) {// Check the validity of Thread *const Self = Thread; assert(Self->is_Java_thread(), "Must be Java thread!" ); JavaThread *jt = (JavaThread *) THREAD; DeferredInitialize(); // Check whether the current thread has the lock CHECK_OWNER(); EventJavaMonitorWait event; If (interruptible && Thread::is_interrupted(Self, true) &&! HAS_PENDING_EXCEPTION) { if (JvmtiExport::should_post_monitor_waited()) { JvmtiExport::post_monitor_waited(jt, this, false); } if (event.should_commit()) { post_monitor_wait_event(&event, 0, millis, false); } TEVENT(Wait - ThrowIEX); THROW(vmSymbols::java_lang_InterruptedException()); return; } TEVENT(Wait); assert(Self->_Stalled == 0, "invariant"); Self->_Stalled = intptr_t(this); jt->set_current_waiting_monitor(this); ObjectWaiter node(Self); node.TState = ObjectWaiter::TS_WAIT; Self->_ParkEvent->reset(); OrderAccess::fence(); // Get a spin lock on waitset. Since only the owner thread operates on waitset (either wait or notify), there is little contention (unless it responds to an interrupt event). Thread::SpinAcquire(&_waitsetLock, "waitset-add "); // Add to waitSet AddWaiter(&node); Thread::SpinRelease(&_waitsetLock); Thread::SpinRelease(&_waitsetLock); if ((SyncFlags & 4) == 0) { _Responsible = NULL; } intptr_t save = _recursions; // record the old recursion count _waiters++; // increment the number of waiters _recursions = 0; // set the recursion level to be 1 exit(true, Self); // exit the monitor guarantee(_owner ! = Self, "invariant"); // Ensure that no unpark incident affects park by actively post unpark if (node._notified! = 0 && _succ == Self) { node._event->unpark(); } // Then the park operation... . }Copy the code

When another owner thread calls notify, the Knob_MoveNotifyee value determines where to put a thread fetched from waitSet (CXQ or EntrySet)

Void ObjectMonitor::notify(TRAPS) {// Check whether the current thread has a lock CHECK_OWNER(); if (_WaitSet == NULL) { TEVENT(Empty - Notify); return; } DTRACE_MONITOR_PROBE(notify, this, object(), THREAD); Int Policy = Knob_MoveNotifyee; Thread::SpinAcquire(&_waitsetLock, "waitset-notify "); ObjectWaiter *iterator = DequeueWaiter(); if (iterator ! = NULL) { TEVENT(Notify1 - Transfer); guarantee(iterator->TState == ObjectWaiter::TS_WAIT, "invariant"); guarantee(iterator->_notified == 0, "invariant"); if (Policy ! = 4) { iterator->TState = ObjectWaiter::TS_ENTER; } iterator->_notified = 1; Thread *Self = THREAD; iterator->_notifier_tid = Self->osthread()->thread_id(); ObjectWaiter *List = _EntryList; if (List ! = NULL) { assert(List->_prev == NULL, "invariant"); assert(List->TState == ObjectWaiter::TS_ENTER, "invariant"); assert(List ! = iterator, "invariant"); } if (Policy == 0) { // prepend to EntryList if (List == NULL) { iterator->_next = iterator->_prev = NULL; _EntryList = iterator; } else { List->_prev = iterator; iterator->_next = List; iterator->_prev = NULL; _EntryList = iterator; } } else if (Policy == 1) { // append to EntryList if (List == NULL) { iterator->_next = iterator->_prev = NULL; _EntryList = iterator; } else { // CONSIDER: finding the tail currently requires a linear-time walk of // the EntryList. We can make tail access constant-time by converting to // a CDLL instead of using our current DLL. ObjectWaiter *Tail; for (Tail = List; Tail->_next ! = NULL; Tail = Tail->_next); assert(Tail ! = NULL && Tail->_next == NULL, "invariant"); Tail->_next = iterator; iterator->_prev = Tail; iterator->_next = NULL; } } else if (Policy == 2) { // prepend to cxq // prepend to cxq if (List == NULL) { iterator->_next = iterator->_prev = NULL; _EntryList = iterator; } else { iterator->TState = ObjectWaiter::TS_CXQ; for (;;) { ObjectWaiter *Front = _cxq; iterator->_next = Front; if (Atomic::cmpxchg_ptr(iterator, &_cxq, Front) == Front) { break; } } } } else if (Policy == 3) { // append to cxq iterator->TState = ObjectWaiter::TS_CXQ; for (;;) { ObjectWaiter *Tail; Tail = _cxq; if (Tail == NULL) { iterator->_next = NULL; if (Atomic::cmpxchg_ptr(iterator, &_cxq, NULL) == NULL) { break; } } else { while (Tail->_next ! = NULL) Tail = Tail->_next; Tail->_next = iterator; iterator->_prev = Tail; iterator->_next = NULL; break; } } } else { ParkEvent *ev = iterator->_event; iterator->TState = ObjectWaiter::TS_RUN; OrderAccess::fence(); ev->unpark(); } if (Policy < 4) { iterator->wait_reenter_begin(this); } // _WaitSetLock protects the wait queue, not the EntryList. We could // move the add-to-EntryList operation, above, outside the critical section // protected by _WaitSetLock. In practice that's not useful. With the // exception of wait() timeouts and interrupts the monitor owner // is the only thread that grabs _WaitSetLock. There's almost no Contention // on _WaitSetLock so it's not profitable to reduce the length of the critical section.} // Releases the Waitset lock  Thread::SpinRelease(&_WaitSetLock); if (iterator ! = NULL && ObjectMonitor::_sync_Notifications ! = NULL) { ObjectMonitor::_sync_Notifications->inc(); }}Copy the code

NotifyAll is a good guess and will not be described here;