說明
- 同步佇列:排隊取鎖的執行緒所在的佇列
- 等待佇列:呼叫
wait
方法後,執行緒會從同步佇列轉移到等待佇列
synchronized 中同步佇列有兩個
_cxq
與EntryList
,基於不同的QMode
來調整執行緒的出隊策略
_cxq(競爭佇列):搶鎖失敗後,執行緒會進入此佇列,此佇列大部分情況時單向連結串列,入隊策略是
後來者當頭
EntryList:預設情況下(根據
Knob_MoveNotifyee
判斷,原始碼預設為 2 ,當 EntryList 不為空,Policy == 2 時,參閱 原始碼 1720-1735行),執行緒被喚醒時,會從等待佇列轉移到此佇列,此佇列是一個雙向連結串列WaitSet:等待佇列,呼叫 wait 方法後,執行緒會進入此佇列
出隊策略
QMode 一共有5種值,0、1、2、3、4,不同的 QMode ,會影響 _cxq 和 EntryList 的優先順序,預設情況下,QMode 為 0
// 當 _cxq 不為空,且 QMode 為 2 時,會直接使用 _cxq 的順序進行喚醒(後來者當頭)
if (QMode == 2 && _cxq != NULL) {
w = _cxq ;
assert (w != NULL, "invariant") ;
assert (w->TState == ObjectWaiter::TS_CXQ, "Invariant") ;
ExitEpilog (Self, w) ;
return ;
}
// 當 _cxq 不為空,且 QMode 為 3 時,將 _cqx 連結到 EntyList 後面
if (QMode == 3 && _cxq != NULL) {
w = _cxq ;
ObjectWaiter * q = NULL ;
ObjectWaiter * p ;
// 將 _cqx 變成雙向連結串列
for (p = w ; p != NULL ; p = p->_next) {
guarantee (p->TState == ObjectWaiter::TS_CXQ, "Invariant") ;
p->TState = ObjectWaiter::TS_ENTER ;
p->_prev = q ;
q = p ;
}
// 將 _cqx 拼在 EntryList 後面
// Append the RATs to the EntryList
// TODO: organize EntryList as a CDLL so we can locate the tail in constant-time.
ObjectWaiter * Tail ;
for (Tail = _EntryList ; Tail != NULL && Tail->_next != NULL ; Tail = Tail->_next) ;
if (Tail == NULL) {
_EntryList = w ;
} else {
Tail->_next = w ;
w->_prev = Tail ;
}
// Fall thru into code that tries to wake a successor from EntryList
}
// 當 _cxq 不為空,且 QMode 為 4 時,將 EntyList 連結到 _cqx 後面
if (QMode == 4 && _cxq != NULL) {
w = _cxq ;
...
// 將 cqx 變為雙向連結串列
ObjectWaiter * q = NULL ;
ObjectWaiter * p ;
for (p = w ; p != NULL ; p = p->_next) {
guarantee (p->TState == ObjectWaiter::TS_CXQ, "Invariant") ;
p->TState = ObjectWaiter::TS_ENTER ;
p->_prev = q ;
q = p ;
}
// 將 EntryList 的頭設為 _cqx
if (_EntryList != NULL) {
q->_next = _EntryList ;
_EntryList->_prev = q ;
}
_EntryList = w ;
}
w = _EntryList ;
// 除了 (Qmode == 2 && _cqx != null) 的情況,按照 EntryList 的順序出隊
if (w != NULL) {
assert (w->TState == ObjectWaiter::TS_ENTER, "invariant") ;
ExitEpilog (Self, w) ;
return ;
}
w = _cxq ;
if (w == NULL) continue ;
// EntryList 為空,QMode 為 1 ,翻轉 _cqx ,然後出隊
if (QMode == 1) {
...
ObjectWaiter * t = w ;
...
// 翻轉 _cqx
while (t != NULL) {
guarantee (t->TState == ObjectWaiter::TS_CXQ, "invariant") ;
t->TState = ObjectWaiter::TS_ENTER ;
u = t->_next ;
t->_prev = u ;
t->_next = s ;
s = t;
t = u ;
}
// 賦值給 _EntryList
_EntryList = s ;
assert (s != NULL, "invariant") ;
} else {
// EntryList 為空,QMode 為 0,直接將 _cqx 賦值給 EntryList,同時變成雙向連結串列
_EntryList = w ;
ObjectWaiter * q = NULL ;
ObjectWaiter * p ;
for (p = w ; p != NULL ; p = p->_next) {
guarantee (p->TState == ObjectWaiter::TS_CXQ, "Invariant") ;
p->TState = ObjectWaiter::TS_ENTER ;
p->_prev = q ;
q = p ;
}
}
// 出隊
w = _EntryList ;
if (w != NULL) {
guarantee (w->TState == ObjectWaiter::TS_ENTER, "invariant") ;
ExitEpilog (Self, w) ;
return ;
}
void ObjectMonitor::ExitEpilog (Thread * Self, ObjectWaiter * Wakee) {
assert (_owner == Self, "invariant") ;
// Exit protocol:
// 1. ST _succ = wakee
// 2. membar #loadstore|#storestore;
// 2. ST _owner = NULL
// 3. unpark(wakee)
_succ = Knob_SuccEnabled ? Wakee->_thread : NULL ;
ParkEvent * Trigger = Wakee->_event ;
// Hygiene -- once we've set _owner = NULL we can't safely dereference Wakee again.
// The thread associated with Wakee may have grabbed the lock and "Wakee" may be
// out-of-scope (non-extant).
Wakee = NULL ;
// Drop the lock
OrderAccess::release_store_ptr (&_owner, NULL) ;
OrderAccess::fence() ; // ST _owner vs LD in unpark()
if (SafepointSynchronize::do_call_back()) {
TEVENT (unpark before SAFEPOINT) ;
}
DTRACE_MONITOR_PROBE(contended__exit, this, object(), Self);
Trigger->unpark() ;
// Maintain stats and report events to JVMTI
if (ObjectMonitor::_sync_Parks != NULL) {
ObjectMonitor::_sync_Parks->inc() ;
}
}
參考資料
本作品採用《CC 協議》,轉載必須註明作者和本文連結