一、先理理所有情況
假設偏向鎖有效,即類沒有關閉偏向模式,且其 epoch 沒有過期。
則一定會發生偏向鎖的撤銷。
第一類情況:線程 B 沒有正在持有該偏向鎖。
- 僅發生單個偏向撤銷,偏向鎖先被撤銷爲無鎖,然後在 slow_enter 裏升級爲輕量級鎖。
- 觸發了批量重偏向,偏向鎖先被撤銷爲匿名偏向,隨後立即重偏向於 A。
- 觸發了批量撤銷,偏向鎖同樣先被撤銷爲無鎖,然後在 slow_enter 裏升級爲輕量級鎖。
第二類情況:線程 B 正在持有該偏向鎖。
- 僅發生單個偏向撤銷,偏向鎖撤銷爲 B 的輕量級鎖,然後在 slow_enter 裏膨脹爲重量級鎖。
- 觸發了批量重偏向,偏向鎖撤銷爲 B 的輕量級鎖,然後在 slow_enter 裏膨脹爲重量級鎖。
- 觸發了批量撤銷,偏向鎖撤銷爲 B 的輕量級鎖,然後在 slow_enter 裏膨脹爲重量級鎖。
情況貌似不止這 6 種,因爲可能在撤銷時,B 正在持有該偏向鎖,但是當撤銷成功後(撤銷過程由 VM 線程在安全點執行,此時 B 被阻塞),它可能立即退出了同步代碼塊,並把鎖從輕量級鎖還原成了無鎖,此時 A 可能是加輕量級鎖成功,也可能是加重量級鎖成功,取決於 A 接下來的加鎖過程中,B 釋放鎖的時機。
還有第一類情況中,撤銷爲無鎖,和之後的加輕量級鎖也是分離的,可能會加鎖失敗,然後膨脹爲重量級鎖。都有可能。
上面所有情況都可以在源碼裏找到對應的處理邏輯。但後面我只會點出前面 6 種對應源碼的處理,其餘情況都可以類似分析可得,不再贅述。
批量重偏向和批量撤銷的觸發條件,可以參考:源碼解析-觸發批量撤銷或批量重偏向的條件
二、從前到後走一遍源碼
HotSpot 有 字節碼解釋器(bytecodeInterpreter) 和 模板解釋器(templateInterpreter),前者是 C++ 實現,後者是彙編實現。
HotSpot 實際使用的是 模板解釋器,沒有用 字節碼解釋器。但兩者邏輯類似,下面從可讀性上考慮,給出 字節碼解釋器 montorenter 指令的解析源碼(說的我好像會彙編一樣,emm):
源碼位置:bytecodeInterpreter.cpp#1816
CASE(_monitorenter): {
oop lockee = STACK_OBJECT(-1);
// derefing's lockee ought to provoke implicit null check
CHECK_NULL(lockee); // lockee 就是鎖對象
// find a free monitor or one already allocated for this object
// if we find a matching object then we need a new monitor
// since this is recursive enter
BasicObjectLock* limit = istate->monitor_base();
BasicObjectLock* most_recent = (BasicObjectLock*) istate->stack_base();
BasicObjectLock* entry = NULL;
// 找空閒的鎖記錄
while (most_recent != limit ) {
if (most_recent->obj() == NULL) entry = most_recent;
else if (most_recent->obj() == lockee) break;
most_recent++;
}
if (entry != NULL) {
entry->set_obj(lockee);
int success = false; // 標誌變量
uintptr_t epoch_mask_in_place = (uintptr_t)markOopDesc::epoch_mask_in_place;
markOop mark = lockee->mark();
intptr_t hash = (intptr_t) markOopDesc::no_hash;
// implies UseBiasedLocking
if (mark->has_bias_pattern()) { // 可偏向的情況
uintptr_t thread_ident;
uintptr_t anticipated_bias_locking_value;
thread_ident = (uintptr_t)istate->thread();
anticipated_bias_locking_value =
(((uintptr_t)lockee->klass()->prototype_header() | thread_ident) ^ (uintptr_t)mark) &
~((uintptr_t) markOopDesc::age_mask_in_place);
if (anticipated_bias_locking_value == 0) {
// already biased towards this thread, nothing to do
// case 1:已經偏向於自身,什麼都不做。。。。。。。。。。。。。。。。。。
if (PrintBiasedLockingStatistics) {
(* BiasedLocking::biased_lock_entry_count_addr())++;
}
success = true;
}
else if ((anticipated_bias_locking_value & markOopDesc::biased_lock_mask_in_place) != 0) {
// try revoke bias
// case 2:試圖撤銷偏向(批量撤銷的漏網之魚)。。。。。。。。。。。。。。。
// 只有這種情況,success 沒有賦爲 true
// 因爲這種情況是 類元數據(klass)禁用了可偏向屬性,即該類發生了批量撤銷
// 對於這種情況,放到後面加輕量級鎖
markOop header = lockee->klass()->prototype_header();
if (hash != markOopDesc::no_hash) {
header = header->copy_set_hash(hash);
}
if (Atomic::cmpxchg_ptr(header, lockee->mark_addr(), mark) == mark) {
if (PrintBiasedLockingStatistics)
(*BiasedLocking::revoked_lock_entry_count_addr())++;
}
}
else if ((anticipated_bias_locking_value & epoch_mask_in_place) !=0) {
// try rebias
// case 3:嘗試重偏向。。。。。。。。。。。。。。。。。。。。。。。。。。
// 這種情況是因爲,對象的 epoch 和 klass 的 epoch 不等
markOop new_header = (markOop) ( (intptr_t) lockee->klass()->prototype_header() | thread_ident);
if (hash != markOopDesc::no_hash) {
new_header = new_header->copy_set_hash(hash);
}
if (Atomic::cmpxchg_ptr((void*)new_header, lockee->mark_addr(), mark) == mark) {
if (PrintBiasedLockingStatistics)
(* BiasedLocking::rebiased_lock_entry_count_addr())++;
}
else {
// 重偏向失敗,說明有另一個線程重偏向成功,需要撤銷偏向,走下面這個調用
CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
}
success = true;
}
else {
// try to bias towards thread in case object is anonymously biased
// case 4:把鎖狀態當成是匿名偏向處理,嘗試偏向於自身。。。。。。。。。。。。
markOop header = (markOop) ((uintptr_t) mark & ((uintptr_t)markOopDesc::biased_lock_mask_in_place |
(uintptr_t)markOopDesc::age_mask_in_place |
epoch_mask_in_place));
if (hash != markOopDesc::no_hash) {
header = header->copy_set_hash(hash);
}
markOop new_header = (markOop) ((uintptr_t) header | thread_ident);
// debugging hint
DEBUG_ONLY(entry->lock()->set_displaced_header((markOop) (uintptr_t) 0xdeaddead);)
if (Atomic::cmpxchg_ptr((void*)new_header, lockee->mark_addr(), header) == header) {
if (PrintBiasedLockingStatistics)
(* BiasedLocking::anonymously_biased_lock_entry_count_addr())++;
}
else {
// 嘗試失敗,兩種可能
// 一是:本來是匿名偏向,被別的線程偏向成功,自己失敗了,需要撤銷偏向
// 二是:本來就不是匿名偏向,而是已經偏向於別的線程,還是需要撤銷偏向
// 撤銷偏向走下面這個調用
CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
}
success = true;
}
}
// traditional lightweight locking
if (!success) { // 走到這不管三七二十一,先加輕量級鎖
markOop displaced = lockee->mark()->set_unlocked();
entry->lock()->set_displaced_header(displaced);
bool call_vm = UseHeavyMonitors;
if (call_vm || Atomic::cmpxchg_ptr(entry, lockee->mark_addr(), displaced) != displaced) {
// Is it simple recursive case?
if (!call_vm && THREAD->is_lock_owned((address) displaced->clear_lock_bits())) {
entry->lock()->set_displaced_header(NULL); // 處理輕量級鎖重入的情況
} else {
// 加輕量級鎖失敗,原因有很多
// 可能是,無鎖加輕量級鎖時,被別的線程先加成功
// 或者,本來就是別人持有的輕量級鎖
// 或者,是重量級鎖
// 還是走下面這個調用處理
CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
}
}
}
UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1);
} else { // 鎖記錄不夠,新建一個鎖記錄後,重新執行
istate->set_msg(more_monitors);
UPDATE_PC_AND_RETURN(0); // Re-execute
}
}
線程請求偏向於另一個線程的偏向鎖,對應 case 4。
會走到這個邏輯:InterpreterRuntime::monitorenter
IRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorenter(JavaThread* thread, BasicObjectLock* elem))
...
Handle h_obj(thread, elem->obj());
...
if (UseBiasedLocking) { // UseBiasedLocking 參數是默認開啓的
// 一般走這裏
ObjectSynchronizer::fast_enter(h_obj, elem->lock(), true, CHECK);
} else {
ObjectSynchronizer::slow_enter(h_obj, elem->lock(), CHECK);
}
...
接着走到 ObjectSynchronizer::fast_enter
// 從 InterpreterRuntime::monitorenter,傳入第三個參數 attempt_rebias 爲 true
void ObjectSynchronizer::fast_enter(Handle obj, BasicLock* lock, bool attempt_rebias, TRAPS) {
if (UseBiasedLocking) {
if (!SafepointSynchronize::is_at_safepoint()) {
// 執行 fast_enter 時不在安全點,一般走這裏
BiasedLocking::Condition cond = BiasedLocking::revoke_and_rebias(obj, attempt_rebias, THREAD);
if (cond == BiasedLocking::BIAS_REVOKED_AND_REBIASED) {
return; // 第一類情況裏的第二種會在這裏直接返回,不會走到後面的 slow_enter
}
} else {
// 執行 fast_enter 時在安全點
...
BiasedLocking::revoke_at_safepoint(obj);
}
...
}
slow_enter (obj, lock, THREAD) ;
}
接下來,先是走到 BiasedLocking::revoke_and_rebias
// 此時,第二個參數 attempt_rebias 爲 true
BiasedLocking::Condition BiasedLocking::revoke_and_rebias(Handle obj, bool attempt_rebias, TRAPS) {
assert(!SafepointSynchronize::is_at_safepoint(), "must not be called while at safepoint");
markOop mark = obj->mark(); // 對象 mark word
if (mark->is_biased_anonymously() && !attempt_rebias) {
// 撤銷匿名偏向,當需要計算對象的 identity hash code 時,會走到這裏
markOop biased_value = mark;
markOop unbiased_prototype = markOopDesc::prototype()->set_age(mark->age());
markOop res_mark = (markOop) Atomic::cmpxchg_ptr(unbiased_prototype, obj->mark_addr(), mark);
if (res_mark == biased_value) {
return BIAS_REVOKED;
}
} else if (mark->has_bias_pattern()) {
Klass* k = obj->klass(); // 對象的類元數據
markOop prototype_header = k->prototype_header();
// 類禁用了偏向模式
if (!prototype_header->has_bias_pattern()) {
markOop biased_value = mark;
markOop res_mark = (markOop) Atomic::cmpxchg_ptr(prototype_header, obj->mark_addr(), mark);
assert(!(*(obj->mark_addr()))->has_bias_pattern(), "even if we raced, should still be revoked");
return BIAS_REVOKED;
// 對象的 epoch 不等於 類的 epoch
} else if (prototype_header->bias_epoch() != mark->bias_epoch()) {
if (attempt_rebias) {
assert(THREAD->is_Java_thread(), "");
markOop biased_value = mark;
markOop rebiased_prototype = markOopDesc::encode((JavaThread*) THREAD, mark->age(), prototype_header->bias_epoch());
markOop res_mark = (markOop) Atomic::cmpxchg_ptr(rebiased_prototype, obj->mark_addr(), mark);
if (res_mark == biased_value) {
return BIAS_REVOKED_AND_REBIASED;
}
} else {
markOop biased_value = mark;
markOop unbiased_prototype = markOopDesc::prototype()->set_age(mark->age());
markOop res_mark = (markOop) Atomic::cmpxchg_ptr(unbiased_prototype, obj->mark_addr(), mark);
if (res_mark == biased_value) {
return BIAS_REVOKED;
}
}
}
}
// 下面準備開始偏向撤銷,首先判斷是否需要批量重偏向或批量撤銷
HeuristicsResult heuristics = update_heuristics(obj(), attempt_rebias);
// 對象不是偏向模式,直接返回
if (heuristics == HR_NOT_BIASED) {
return NOT_BIASED;
// 單個偏向的撤銷
} else if (heuristics == HR_SINGLE_REVOKE) {
Klass *k = obj->klass();
markOop prototype_header = k->prototype_header();
// 偏向所有者是自身,且 epoch 沒有過期
if (mark->biased_locker() == THREAD &&
prototype_header->bias_epoch() == mark->bias_epoch()) {
// 這個分支用於處理,有效地偏向所有者要計算對象 identity hash code 的情況
// 單獨處理是因爲,這種情況在線程自己的棧處理就好了,不需要等到 safe point
ResourceMark rm;
if (TraceBiasedLocking) {
tty->print_cr("Revoking bias by walking my own stack:");
}
BiasedLocking::Condition cond = revoke_bias(obj(), false, false, (JavaThread*) THREAD);
((JavaThread*) THREAD)->set_cached_monitor_info(NULL);
assert(cond == BIAS_REVOKED, "why not?");
return cond;
} else {
// 否則,讓 VM 線程在 safepoint 調用 VM_RevokeBias 的 doit 方法撤銷偏向
VM_RevokeBias revoke(&obj, (JavaThread*) THREAD);
VMThread::execute(&revoke);
return revoke.status_code();
}
}
assert((heuristics == HR_BULK_REVOKE) ||
(heuristics == HR_BULK_REBIAS), "?");
// 如果走到這裏,說明觸發了批量重偏向或批量撤銷
// VM 線程在 safepoint 調用 VM_BulkRevokeBias 的 doit 方法
VM_BulkRevokeBias bulk_revoke(&obj, (JavaThread*) THREAD,
(heuristics == HR_BULK_REBIAS),
attempt_rebias); // 此時,這個參數爲 true
VMThread::execute(&bulk_revoke);
return bulk_revoke.status_code();
}
看一下 VM_RevokeBias 的 doit 方法(可以將 VM 線程的 doit 方法類比於 Java 線程的 run 方法)
virtual void doit() {
if (_obj != NULL) {
if (TraceBiasedLocking) {
tty->print_cr("Revoking bias with potentially per-thread safepoint:");
}
// 鎖對象不爲 NULL,調用 revoke_bias 撤銷偏向
_status_code = revoke_bias((*_obj)(), false, false, _requesting_thread);
clean_up_cached_monitor_info();
return;
} else {
if (TraceBiasedLocking) {
tty->print_cr("Revoking bias with global safepoint:");
}
BiasedLocking::revoke_at_safepoint(_objs);
}
}
單個對象的偏向撤銷,可以參考:源碼解析-偏向鎖撤銷流程解讀
撤銷結果是:
如果偏向所有者,也就是線程 B,它正在持有該偏向鎖,則將該偏向鎖撤銷爲 B 持有的輕量級鎖。
如果線程 B 沒有正在持有該偏向鎖,則將該偏向鎖撤銷爲無鎖。
這兩種情況都會在隨後的 slow_enter 方法進行鎖升級。(前面 ObjectSynchronizer::fast_enter 方法裏的邏輯)
再看看 VM_BulkRevokeBias 的 doit 方法
virtual void doit() {
_status_code = bulk_revoke_or_rebias_at_safepoint((*_obj)(), _bulk_rebias, _attempt_rebias_of_object, _requesting_thread);
clean_up_cached_monitor_info();
}
如果發生了批量重偏向或批量撤銷,VM 線程會執行到 BiasedLocking::Condition bulk_revoke_or_rebias_at_safepoint
static BiasedLocking::Condition bulk_revoke_or_rebias_at_safepoint(oop o,
bool bulk_rebias,
bool attempt_rebias_of_object,
JavaThread* requesting_thread) {
...
// 當前時間
jlong cur_time = os::javaTimeMillis();
// 將這次重偏向時間寫入類元數據,作爲下次觸發批量重偏向或批量撤銷的啓發條件之一
o->klass()->set_last_biased_lock_bulk_revocation_time(cur_time);
Klass* k_o = o->klass(); // 觸發重偏向對象的 類元數據
Klass* klass = k_o;
if (bulk_rebias) {
// 這個分支是批量重偏向的邏輯
// 類開啓了偏向模式,才進行批量重偏向
if (klass->prototype_header()->has_bias_pattern()) {
int prev_epoch = klass->prototype_header()->bias_epoch();
// 自增類的 epoch
klass->set_prototype_header(klass->prototype_header()->incr_bias_epoch());
// 獲取類自增後的 epoch
int cur_epoch = klass->prototype_header()->bias_epoch();
// 遍歷所有線程
for (JavaThread* thr = Threads::first(); thr != NULL; thr = thr->next()) {
GrowableArray<MonitorInfo*>* cached_monitor_info = get_or_compute_monitor_info(thr);
// 遍歷線程所有的鎖記錄
for (int i = 0; i < cached_monitor_info->length(); i++) {
MonitorInfo* mon_info = cached_monitor_info->at(i);
oop owner = mon_info->owner();
markOop mark = owner->mark();
// 找到所有當前類的偏向鎖對象
if ((owner->klass() == k_o) && mark->has_bias_pattern()) {
// We might have encountered this object already in the case of recursive locking
assert(mark->bias_epoch() == prev_epoch || mark->bias_epoch() == cur_epoch, "error in bias epoch adjustment");
// 更新該類偏向鎖對象的 epoch 與 類的 epoch 保持一致
owner->set_mark(mark->set_bias_epoch(cur_epoch));
}
}
}
}
// 撤銷當前偏向鎖
// 此時,由於參數 attempt_rebias_of_object 爲 true
// 所以,如果偏向所有者沒有正在持有該偏向鎖,該鎖會撤銷爲匿名偏向鎖
// 否則,撤銷爲輕量級鎖
revoke_bias(o, attempt_rebias_of_object && klass->prototype_header()->has_bias_pattern(), true, requesting_thread);
} else {
...
// 這個分支是批量撤銷的邏輯
// 首先,禁用 類元數據 裏的可偏向屬性
// markOopDesc::prototype() 返回的是一個關閉偏向模式的 prototype
klass->set_prototype_header(markOopDesc::prototype());
// 其次,遍歷所有線程的棧,撤銷該類正在被持有的偏向鎖爲輕量級鎖
for (JavaThread* thr = Threads::first(); thr != NULL; thr = thr->next()) {
GrowableArray<MonitorInfo*>* cached_monitor_info = get_or_compute_monitor_info(thr);
for (int i = 0; i < cached_monitor_info->length(); i++) {
MonitorInfo* mon_info = cached_monitor_info->at(i);
oop owner = mon_info->owner();
markOop mark = owner->mark();
if ((owner->klass() == k_o) && mark->has_bias_pattern()) {
// 具體撤銷,還是通過 revoke_bias() 方法去做
revoke_bias(owner, false, true, requesting_thread);
}
}
}
// 當前鎖對象可能未被任何線程持有
// 所以這裏單獨進行撤銷,以確保完成調用方的撤銷語義
revoke_bias(o, false, true, requesting_thread);
}
...
BiasedLocking::Condition status_code = BiasedLocking::BIAS_REVOKED;
if (attempt_rebias_of_object &&
o->mark()->has_bias_pattern() &&
klass->prototype_header()->has_bias_pattern()) {
// 參數 attempt_rebias_of_object 爲 true
// 並且發生的是批量重偏向
// 而且偏向所有者沒有正在持有該偏向鎖,會走到這裏
// 如果滿足上面三個條件,則說明當前鎖對象已經被撤銷爲了匿名偏向
// 下面直接讓該鎖重偏向於當前線程
markOop new_mark = markOopDesc::encode(requesting_thread, o->mark()->age(),
klass->prototype_header()->bias_epoch());
o->set_mark(new_mark); // 由於是在安全點裏,所以不需要 CAS 操作
// 注意這裏的返回值,這個返回值傳到 fast_enter 方法後
// fast_enter 方法就直接返回了,而不會執行到後面的 slow_enter 方法
status_code = BiasedLocking::BIAS_REVOKED_AND_REBIASED;
...
}
}
...
return status_code;
}
此時,第一類情況裏的第二種已經加鎖完成。
剩下的所有情況,都要接着走到 slow_enter 裏進行鎖升級。
ObjectSynchronizer::slow_enter 源碼如下:
void ObjectSynchronizer::slow_enter(Handle obj, BasicLock* lock, TRAPS) {
markOop mark = obj->mark();
assert(!mark->has_bias_pattern(), "should not see bias pattern here");
// 無鎖的情況,嘗試加輕量級鎖,成功後返回
if (mark->is_neutral()) {
// 設置鎖記錄的 displaced mark word
lock->set_displaced_header(mark);
// 嘗試將鎖記錄的地址寫入對象的 mark word
if (mark == (markOop) Atomic::cmpxchg_ptr(lock, obj()->mark_addr(), mark)) {
TEVENT (slow_enter: release stacklock) ;
return ; // 第一類情況的第一和第三種,在這裏加鎖成功後返回
}
// Fall through to inflate() ...
} else
// 處理輕量級鎖重入的情況
if (mark->has_locker() && THREAD->is_lock_owned((address)mark->locker())) {
assert(lock != mark->locker(), "must not re-lock the same lock");
assert(lock != (BasicLock*)obj->mark(), "don't relock with same BasicLock");
lock->set_displaced_header(NULL);
return;
}
#if 0
// The following optimization isn't particularly useful.
if (mark->has_monitor() && mark->monitor()->is_entered(THREAD)) {
lock->set_displaced_header (NULL) ;
return ;
}
#endif
// The object header will never be displaced to this lock,
// so it does not matter what the value is, except that it
// must be non-zero to avoid looking like a re-entrant lock,
// and must not look locked either.
lock->set_displaced_header(markOopDesc::unused_mark());
// 這裏先是調用 inflate 方法將鎖膨脹爲重量級鎖
// 然後又調用其返回對象的 enter 方法處理重量級鎖的獲取請求
ObjectSynchronizer::inflate(THREAD, obj())->enter(THREAD);
}
此時,第一類情況的第一和第三種,如果在這裏成功加上輕量級鎖,就可以返回了。
其餘情況需要接着走到 ObjectSynchronizer::inflate ,該方法會爲鎖對象分配 ObjectMonitor 對象(就是我們常說的重量級鎖的 monitor),然後 slow_enter 調用 ObjectMonitor 的 enter 方法處理重量級鎖的獲取請求。
ObjectMonitor * ATTR ObjectSynchronizer::inflate (Thread * Self, oop object) {
// Inflate mutates the heap ...
// Relaxing assertion for bug 6320749.
assert (Universe::verify_in_progress() ||
!SafepointSynchronize::is_at_safepoint(), "invariant") ;
for (;;) {
const markOop mark = object->mark() ;
assert (!mark->has_bias_pattern(), "invariant") ;
// The mark can be in one of the following states:
// * Inflated - just return
// * Stack-locked - coerce it to inflated
// * INFLATING - busy wait for conversion to complete
// * Neutral - aggressively inflate the object.
// * BIASED - Illegal. We should never see this
// 如果已經膨脹成功,則直接返回
if (mark->has_monitor()) {
...
return inf ;
}
// 如果有其它線程先一步正在膨脹當前鎖對象,當前線程只能等待其膨脹完成
if (mark == markOopDesc::INFLATING()) {
TEVENT (Inflate: spin while INFLATING) ;
ReadStableMark(object) ;
continue ;
}
// 輕量級鎖膨脹的情況
if (mark->has_locker()) {
// 爲鎖對象分配一個 objectmonitor 對象,並初始化其值
ObjectMonitor * m = omAlloc (Self) ;
m->Recycle();
m->_Responsible = NULL ;
m->OwnerIsThread = 0 ;
m->_recursions = 0 ;
m->_SpinDuration = ObjectMonitor::Knob_SpinLimit ; // Consider: maintain by type/class
// 嘗試在對象 mark word 寫入 INFLATING 標誌
markOop cmp = (markOop) Atomic::cmpxchg_ptr (markOopDesc::INFLATING(), object->mark_addr(), mark) ;
// 寫入成功,則獲得膨脹資格,否則其它線程獲得膨脹資格,當前線程只能等待
if (cmp != mark) {
omRelease (Self, m, true) ;
continue ; // Interference -- just retry
}
// 源碼這裏解釋了爲什麼要有一個 INFLATING 狀態
// We've successfully installed INFLATING (0) into the mark-word.
// This is the only case where 0 will appear in a mark-work.
// Only the singular thread that successfully swings the mark-word
// to 0 can perform (or more precisely, complete) inflation.
//
// Why do we CAS a 0 into the mark-word instead of just CASing the
// mark-word from the stack-locked value directly to the new inflated state?
// Consider what happens when a thread unlocks a stack-locked object.
// It attempts to use CAS to swing the displaced header value from the
// on-stack basiclock back into the object header. Recall also that the
// header value (hashcode, etc) can reside in (a) the object header, or
// (b) a displaced header associated with the stack-lock, or (c) a displaced
// header in an objectMonitor. The inflate() routine must copy the header
// value from the basiclock on the owner's stack to the objectMonitor, all
// the while preserving the hashCode stability invariants. If the owner
// decides to release the lock while the value is 0, the unlock will fail
// and control will eventually pass from slow_exit() to inflate. The owner
// will then spin, waiting for the 0 value to disappear. Put another way,
// the 0 causes the owner to stall if the owner happens to try to
// drop the lock (restoring the header from the basiclock to the object)
// while inflation is in-progress. This protocol avoids races that might
// would otherwise permit hashCode values to change or "flicker" for an object.
// Critically, while object->mark is 0 mark->displaced_mark_helper() is stable.
// 0 serves as a "BUSY" inflate-in-progress indicator.
// The owner can't die or unwind past the lock while our INFLATING
// object is in the mark. Furthermore the owner can't complete
// an unlock on the object, either.
// 從所有者的棧中獲取 displaced mark
markOop dmw = mark->displaced_mark_helper() ;
assert (dmw->is_neutral(), "invariant") ;
m->set_header(dmw) ;
// 設置監視器的 owner 爲對象 mark word 指向的鎖記錄
m->set_owner(mark->locker());
m->set_object(object);
guarantee (object->mark() == markOopDesc::INFLATING(), "invariant") ;
// 設置對象 mark word 爲重量級鎖狀態
object->release_set_mark(markOopDesc::encode(m));
...
return m ;
}
// 無鎖狀態膨脹的情況
assert (mark->is_neutral(), "invariant");
ObjectMonitor * m = omAlloc (Self) ;
// prepare m for installation - set monitor to initial state
m->Recycle();
m->set_header(mark);
m->set_owner(NULL);
m->set_object(object);
m->OwnerIsThread = 1 ;
m->_recursions = 0 ;
m->_Responsible = NULL ;
m->_SpinDuration = ObjectMonitor::Knob_SpinLimit ; // consider: keep metastats by type/class
if (Atomic::cmpxchg_ptr (markOopDesc::encode(m), object->mark_addr(), mark) != mark) {
// 替換對象頭的 mark word 爲重量級鎖狀態不成功
// 說明有另外一個線程膨脹成功,釋放 monitor 對象
m->set_object (NULL) ;
m->set_owner (NULL) ;
m->OwnerIsThread = 0 ;
m->Recycle() ;
omRelease (Self, m, true) ;
m = NULL ;
continue ;
}
...
return m ;
}
}
隨後來到 ObjectMonitor::enter
void ATTR ObjectMonitor::enter(TRAPS) {
Thread * const Self = THREAD ;
void * cur ;
// 重量級鎖的 owner 爲 NULL,表示當前重量級鎖是剛從無鎖狀態膨脹上來的
// 如果能 CAS 設置成功,則當前線程直接獲得鎖
cur = Atomic::cmpxchg_ptr (Self, &_owner, NULL) ;
if (cur == NULL) {
// 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 (cur == Self) {
// TODO-FIXME: check for integer overflow! BUGID 6557169.
_recursions ++ ;
return ;
}
// 如果重量級鎖的 owner 指向當前線程的鎖記錄
// 則說明當前重量級鎖是剛從當前線程持有的輕量級鎖膨脹上來的
// 當前線程已經持有了該重量級鎖,做一些設置後即可返回
if (Self->is_lock_owned ((address)cur)) {
assert (_recursions == 0, "internal state error");
// 初始化重入計數
_recursions = 1 ;
// 把原本指向線程鎖記錄的 owner,改成指向線程
_owner = Self ;
OwnerIsThread = 1 ;
return ;
}
// We've encountered genuine contention.
assert (Self->_Stalled == 0, "invariant") ;
Self->_Stalled = intptr_t(this) ;
// 先嚐試自旋獲取鎖
if (Knob_SpinEarly && TrySpin (Self) > 0) {
assert (_owner == Self , "invariant") ;
assert (_recursions == 0 , "invariant") ;
assert (((oop)(object()))->mark() == markOopDesc::encode(this), "invariant") ;
Self->_Stalled = 0 ;
return ;
}
...
// Prevent deflation at STW-time. See deflate_idle_monitors() and is_busy().
// Ensure the object-monitor relationship remains stable while there's contention.
Atomic::inc_ptr(&_count);
EventJavaMonitorEnter event;
{ // Change java thread status to indicate blocked on monitor enter.
// 更改 java 線程狀態
JavaThreadBlockedOnMonitorEnterState jtbmes(jt, this);
Self->set_current_pending_monitor(this);
DTRACE_MONITOR_PROBE(contended__enter, this, object(), jt);
if (JvmtiExport::should_post_monitor_contended_enter()) {
JvmtiExport::post_monitor_contended_enter(jt, this);
}
OSThreadContendState osts(Self->osthread());
ThreadBlockInVM tbivm(jt);
// TODO-FIXME: change the following for(;;) loop to straight-line code.
for (;;) {
jt->set_suspend_equivalent();
// 在該方法中決定當前線程是獲取鎖還是進行系統調用掛起當前線程
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) ;
jt->java_suspend_self();
}
Self->set_current_pending_monitor(NULL);
}
Atomic::dec_ptr(&_count);
assert (_count >= 0, "invariant") ;
Self->_Stalled = 0 ;
...
DTRACE_MONITOR_PROBE(contended__entered, this, object(), jt);
if (JvmtiExport::should_post_monitor_contended_entered()) {
JvmtiExport::post_monitor_contended_entered(jt, this);
}
if (event.should_commit()) {
event.set_klass(((oop)this->object())->klass());
event.set_previousOwner((TYPE_JAVALANGTHREAD)_previous_owner_tid);
event.set_address((TYPE_ADDRESS)(uintptr_t)(this->object_addr()));
event.commit();
}
if (ObjectMonitor::_sync_ContendedLockAttempts != NULL) {
ObjectMonitor::_sync_ContendedLockAttempts->inc() ;
}
}
最後在 ObjectMonitor::EnterI 方法裏,要麼當前線程獲取到重量級鎖,要麼進行系統調用掛起當前線程。
參考:
- openjdk8 的源碼
- 死磕Synchronized底層實現 系列