ObjectWaiter * volatile _WaitSet; 双向链表的表头。记录等待在monitor对象的线程列表 LL of threads wait()ing on the monitor
ObjectWaiter * volatile _EntryList ; 准备获取monitor对象的线程,但是没有获取到 Threads blocked on entry or reentry.
void * volatile _owner; pointer to owning thread OR BasicLock
volatile intptr_t _recursions; recursion count, 0 for first entry 可重入锁
ObjectWaiter * volatile _cxq LL of recently-arrived threads blocked on entry. The list is actually composed of WaitNodes, acting as proxies for Threads.
volatile int _WaitSetLock; 自旋锁, protects Wait Queue - simple spinlock
// Enter the waiting queue, which is a circular doubly linked list in this case // but it could be a priority queue or any data structure. // _WaitSetLock protects the wait queue. Normally the wait queue is accessed only // by the the owner of the monitor *except* in the case where park() // returns because of a timeout of interrupt. Contention is exceptionally rare // so we use a simple spin-lock instead of a heavier-weight blocking lock.
intptr_t save = _recursions; // record the old recursion count _waiters++; // 4. 等待的线程个数加1 exit (true, Self) ; // 5. 当前线程退出监视器,释放monitor guarantee (_owner != Self, "invariant") ; // 确保当前线程已经释放掉了monitor
// The thread is on the WaitSet list - now park() it. // 6. 调用OS的api将当前线程 转换成wait状态 (park) int ret = OS_OK ; int WasNotified = 0 ; { // State transition wrappers OSThread* osthread = Self->osthread(); OSThreadWaitState osts(osthread, true); { ThreadBlockInVM tbivm(jt); // Thread is in thread_blocked state and oop access is unsafe. jt->set_suspend_equivalent();
if (interruptible && (Thread::is_interrupted(THREAD, false) || HAS_PENDING_EXCEPTION)) { // Intentionally empty } else if (node._notified == 0) { if (millis <= 0) { Self->_ParkEvent->park () ; } else { ret = Self->_ParkEvent->park (millis) ; } } if (ExitSuspendEquivalent (jt)) { jt->java_suspend_self(); }
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. }
Thread::SpinRelease (&_WaitSetLock) ; }
inline ObjectWaiter* ObjectMonitor::DequeueWaiter(){ // 简单 // dequeue the very first waiter ObjectWaiter* waiter = _WaitSet; if (waiter) { DequeueSpecificWaiter(waiter); } return waiter; }
inlinevoidObjectMonitor::DequeueSpecificWaiter(ObjectWaiter* node){ // 简单 // when the waiter has woken up because of interrupt, // timeout or other spurious wake-up, dequeue the // waiter from waiting list ObjectWaiter* next = node->_next; if (next == node) { _WaitSet = NULL; } else { ObjectWaiter* prev = node->_prev; next->_prev = prev; prev->_next = next; if (_WaitSet == node) { _WaitSet = next; } } node->_next = NULL; node->_prev = NULL; }
void ATTR ObjectMonitor::enter(TRAPS) { // The following code is ordered to check the most common cases first Thread * constSelf= THREAD ; void * cur ;
cur = Atomic::cmpxchg_ptr (Self, &_owner, NULL) ; // 1. 竞争锁 if (cur == NULL) { // 返回旧值:CAS成功,Self线程竞争到锁,owner设置成功 // Either ASSERT _recursions == 0 or explicitly set _recursions = 0. assert (_recursions == 0 , "invariant") ; assert (_owner == Self, "invariant") ; // CONSIDER: set or assert OwnerIsThread == 1 return ; }
if (Self->is_lock_owned ((address)cur)) { // 3. owner除了是线程,还可能是一个基本锁对象 assert (_recursions == 0, "internal state error"); _recursions = 1 ; // Commute owner from a thread-specific on-stack BasicLockObject address to // a full-fledged "Thread *". _owner = Self ; OwnerIsThread = 1 ; return ; } // else: 4. 没有竞争到锁,其他线程持有了锁。当前线程,先自旋,不行再阻塞
// Try one round of spinning *before* enqueueing Self // and before going through the awkward and expensive state // transitions. The following spin is strictly optional ... // Note that if we acquire the monitor from an initial spin // we forgo posting JVMTI events and firing DTRACE probes. if (Knob_SpinEarly && TrySpin (Self) > 0) { // 5. 尝试自旋,如果能获取到锁就更好 assert (_owner == Self , "invariant") ; assert (_recursions == 0 , "invariant") ; assert (((oop)(object()))->mark() == markOopDesc::encode(this), "invariant") ; Self->_Stalled = 0 ; return ; }
// TODO-FIXME: change the following for(;;) loop to straight-line code. for (;;) { jt->set_suspend_equivalent(); // cleared by handle_special_suspend_equivalent_condition() // or java_suspend_self() EnterI (THREAD) ; if (!ExitSuspendEquivalent(jt)) break ; // // We have acquired the contended monitor, but while we were // waiting another thread suspended us. We don't want to enter // the monitor while suspended because that would surprise the // thread that suspended us. // _recursions = 0 ; _succ = NULL ; exit (false, Self) ;
// Push "Self" onto the front of the _cxq. // Once on cxq/EntryList, Self stays on-queue until it acquires the lock. // Note that spinning tends to reduce the rate at which threads // enqueue and dequeue on EntryList|cxq. ObjectWaiter * nxt ; for (;;) { node._next = nxt = _cxq ; if (Atomic::cmpxchg_ptr (&node, &_cxq, nxt) == nxt) break ;
// Interference - the CAS failed because _cxq changed. Just retry. // As an optional optimization we retry the lock. if (TryLock (Self) > 0) { assert (_succ != Self , "invariant") ; assert (_owner == Self , "invariant") ; assert (_Responsible != Self , "invariant") ; return ; } }
// Check for cxq|EntryList edge transition to non-null. This indicates // the onset of contention. // The lock have been released while this thread was occupied queueing // itself onto _cxq. To close the race and avoid "stranding" and // progress-liveness failure we must resample-retry _owner before parking. // Note the Dekker/Lamport duality: ST cxq; MEMBAR; LD Owner. // In this case the ST-MEMBAR is accomplished with CAS(). // // TODO: Defer all thread state transitions until park-time. // Since state transitions are heavy and inefficient we'd like // to defer the state transitions until absolutely necessary, // and in doing so avoid some transitions ...
for (;;) { if (TryLock (Self) > 0) break ; assert (_owner != Self, "invariant") ; // park self if (_Responsible == Self || (SyncFlags & 1)) { TEVENT (Inflated enter - park TIMED) ; Self->_ParkEvent->park ((jlong) RecheckInterval) ; // Increase the RecheckInterval, but clamp the value. RecheckInterval *= 8 ; if (RecheckInterval > 1000) RecheckInterval = 1000 ; } else { TEVENT (Inflated enter - park UNTIMED) ; Self->_ParkEvent->park() ; // 将当前线程阻塞 }