本文為死磕Synchronized底層實(shí)現(xiàn)第三篇文章,內(nèi)容為輕量級(jí)鎖實(shí)現(xiàn)。
輕量級(jí)鎖并不復(fù)雜迫淹,其中很多內(nèi)容在偏向鎖一文中已提及過(guò),與本文內(nèi)容會(huì)有部分重疊为严。
另外輕量級(jí)鎖的背景和基本流程在概論中已有講解敛熬。強(qiáng)烈建議在看過(guò)兩篇文章的基礎(chǔ)下閱讀本文。
本系列文章將對(duì)HotSpot的synchronized
鎖實(shí)現(xiàn)進(jìn)行全面分析第股,內(nèi)容包括偏向鎖应民、輕量級(jí)鎖、重量級(jí)鎖的加鎖、解鎖诲锹、鎖升級(jí)流程的原理及源碼分析繁仁,希望給在研究synchronized
路上的同學(xué)一些幫助。主要包括以下幾篇文章:
死磕Synchronized底層實(shí)現(xiàn)--概論
死磕Synchronized底層實(shí)現(xiàn)--偏向鎖
死磕Synchronized底層實(shí)現(xiàn)--輕量級(jí)鎖
死磕Synchronized底層實(shí)現(xiàn)--重量級(jí)鎖
更多文章見(jiàn)個(gè)人博客:https://github.com/farmerjohngit/myblog
本文分為兩個(gè)部分:
1.輕量級(jí)鎖獲取流程
2.輕量級(jí)鎖釋放流程
本人看的JVM版本是jdk8u归园,具體版本號(hào)以及代碼可以在這里看到黄虱。
輕量級(jí)鎖獲取流程
下面開(kāi)始輕量級(jí)鎖獲取流程分析,代碼在bytecodeInterpreter.cpp#1816庸诱。
CASE(_monitorenter): {
oop lockee = STACK_OBJECT(-1);
...
if (entry != NULL) {
...
// 上面省略的代碼中如果CAS操作失敗也會(huì)調(diào)用到InterpreterRuntime::monitorenter
// traditional lightweight locking
if (!success) {
// 構(gòu)建一個(gè)無(wú)鎖狀態(tài)的Displaced Mark Word
markOop displaced = lockee->mark()->set_unlocked();
// 設(shè)置到Lock Record中去
entry->lock()->set_displaced_header(displaced);
bool call_vm = UseHeavyMonitors;
if (call_vm || Atomic::cmpxchg_ptr(entry, lockee->mark_addr(), displaced) != displaced) {
// 如果CAS替換不成功悬钳,代表鎖對(duì)象不是無(wú)鎖狀態(tài),這時(shí)候判斷下是不是鎖重入
// Is it simple recursive case?
if (!call_vm && THREAD->is_lock_owned((address) displaced->clear_lock_bits())) {
entry->lock()->set_displaced_header(NULL);
} else {
// CAS操作失敗則調(diào)用monitorenter
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
}
}
如果鎖對(duì)象不是偏向模式或已經(jīng)偏向其他線程偶翅,則success
為false
。這時(shí)候會(huì)構(gòu)建一個(gè)無(wú)鎖狀態(tài)的mark word
設(shè)置到Lock Record
中去碉渡,我們稱(chēng)Lock Record
中存儲(chǔ)對(duì)象mark word
的字段叫Displaced Mark Word
聚谁。
如果當(dāng)前鎖的狀態(tài)不是無(wú)鎖狀態(tài),則CAS失敗滞诺。如果這是一次鎖重入形导,那直接將Lock Record
的 Displaced Mark Word
設(shè)置為null
。
我們看個(gè)demo习霹,在該demo中重復(fù)3次獲得鎖朵耕,
synchronized(obj){
synchronized(obj){
synchronized(obj){
}
}
}
假設(shè)鎖的狀態(tài)是輕量級(jí)鎖,下圖反應(yīng)了mark word
和線程棧中Lock Record
的狀態(tài)淋叶,可以看到右邊線程棧中包含3個(gè)指向當(dāng)前鎖對(duì)象的Lock Record
阎曹。其中棧中最高位的Lock Record
為第一次獲取鎖時(shí)分配的。其Displaced Mark word
的值為鎖對(duì)象的加鎖前的mark word
煞檩,之后的鎖重入會(huì)在線程棧中分配一個(gè)Displaced Mark word
為null
的Lock Record
处嫌。
為什么JVM選擇在線程棧中添加Displaced Mark word
為null的Lock Record
來(lái)表示重入計(jì)數(shù)呢?首先鎖重入次數(shù)是一定要記錄下來(lái)的斟湃,因?yàn)槊看谓怄i都需要對(duì)應(yīng)一次加鎖熏迹,解鎖次數(shù)等于加鎖次數(shù)時(shí),該鎖才真正的被釋放凝赛,也就是在解鎖時(shí)需要用到說(shuō)鎖重入次數(shù)的注暗。一個(gè)簡(jiǎn)單的方案是將鎖重入次數(shù)記錄在對(duì)象頭的mark word
中,但mark word
的大小是有限的墓猎,已經(jīng)存放不下該信息了捆昏。另一個(gè)方案是只創(chuàng)建一個(gè)Lock Record
并在其中記錄重入次數(shù),Hotspot沒(méi)有這樣做的原因我猜是考慮到效率有影響:每次重入獲得鎖都需要遍歷該線程的棧找到對(duì)應(yīng)的Lock Record
毙沾,然后修改它的值屡立。
所以最終Hotspot選擇每次獲得鎖都添加一個(gè)Lock Record
來(lái)表示鎖的重入。
接下來(lái)看看InterpreterRuntime::monitorenter
方法
IRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorenter(JavaThread* thread, BasicObjectLock* elem))
...
Handle h_obj(thread, elem->obj());
assert(Universe::heap()->is_in_reserved_or_null(h_obj()),
"must be NULL or an object");
if (UseBiasedLocking) {
// Retry fast entry if bias is revoked to avoid unnecessary inflation
ObjectSynchronizer::fast_enter(h_obj, elem->lock(), true, CHECK);
} else {
ObjectSynchronizer::slow_enter(h_obj, elem->lock(), CHECK);
}
...
IRT_END
fast_enter
的流程在偏向鎖一文已經(jīng)分析過(guò),如果當(dāng)前是偏向模式且偏向的線程還在使用鎖膨俐,那會(huì)將鎖的mark word
改為輕量級(jí)鎖的狀態(tài)勇皇,同時(shí)會(huì)將偏向的線程棧中的Lock Record
修改為輕量級(jí)鎖對(duì)應(yīng)的形式。代碼位置在biasedLocking.cpp#212焚刺。
// 線程還存活則遍歷線程棧中所有的Lock Record
GrowableArray<MonitorInfo*>* cached_monitor_info = get_or_compute_monitor_info(biased_thread);
BasicLock* highest_lock = NULL;
for (int i = 0; i < cached_monitor_info->length(); i++) {
MonitorInfo* mon_info = cached_monitor_info->at(i);
// 如果能找到對(duì)應(yīng)的Lock Record說(shuō)明偏向的線程還在執(zhí)行同步代碼塊中的代碼
if (mon_info->owner() == obj) {
...
// 需要升級(jí)為輕量級(jí)鎖敛摘,直接修改偏向線程棧中的Lock Record。為了處理鎖重入的case乳愉,在這里將Lock Record的Displaced Mark Word設(shè)置為null兄淫,第一個(gè)Lock Record會(huì)在下面的代碼中再處理
markOop mark = markOopDesc::encode((BasicLock*) NULL);
highest_lock = mon_info->lock();
highest_lock->set_displaced_header(mark);
} else {
...
}
}
if (highest_lock != NULL) {
// 修改第一個(gè)Lock Record為無(wú)鎖狀態(tài),然后將obj的mark word設(shè)置為執(zhí)行該Lock Record的指針
highest_lock->set_displaced_header(unbiased_prototype);
obj->release_set_mark(markOopDesc::encode(highest_lock));
...
} else {
...
}
我們看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");
// 如果是無(wú)鎖狀態(tài)
if (mark->is_neutral()) {
//設(shè)置Displaced Mark Word并替換對(duì)象頭的mark word
lock->set_displaced_header(mark);
if (mark == (markOop) Atomic::cmpxchg_ptr(lock, obj()->mark_addr(), mark)) {
TEVENT (slow_enter: release stacklock) ;
return ;
}
} 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");
// 如果是重入捕虽,則設(shè)置Displaced Mark Word為null
lock->set_displaced_header(NULL);
return;
}
...
// 走到這一步說(shuō)明已經(jīng)是存在多個(gè)線程競(jìng)爭(zhēng)鎖了 需要膨脹為重量級(jí)鎖
lock->set_displaced_header(markOopDesc::unused_mark());
ObjectSynchronizer::inflate(THREAD, obj())->enter(THREAD);
}
輕量級(jí)鎖釋放流程
CASE(_monitorexit): {
oop lockee = STACK_OBJECT(-1);
CHECK_NULL(lockee);
// derefing's lockee ought to provoke implicit null check
// find our monitor slot
BasicObjectLock* limit = istate->monitor_base();
BasicObjectLock* most_recent = (BasicObjectLock*) istate->stack_base();
// 從低往高遍歷棧的Lock Record
while (most_recent != limit ) {
// 如果Lock Record關(guān)聯(lián)的是該鎖對(duì)象
if ((most_recent)->obj() == lockee) {
BasicLock* lock = most_recent->lock();
markOop header = lock->displaced_header();
// 釋放Lock Record
most_recent->set_obj(NULL);
// 如果是偏向模式,僅僅釋放Lock Record就好了坡脐。否則要走輕量級(jí)鎖or重量級(jí)鎖的釋放流程
if (!lockee->mark()->has_bias_pattern()) {
bool call_vm = UseHeavyMonitors;
// header!=NULL說(shuō)明不是重入泄私,則需要將Displaced Mark Word CAS到對(duì)象頭的Mark Word
if (header != NULL || call_vm) {
if (call_vm || Atomic::cmpxchg_ptr(header, lockee->mark_addr(), lock) != lock) {
// CAS失敗或者是重量級(jí)鎖則會(huì)走到這里,先將obj還原备闲,然后調(diào)用monitorexit方法
most_recent->set_obj(lockee);
CALL_VM(InterpreterRuntime::monitorexit(THREAD, most_recent), handle_exception);
}
}
}
//執(zhí)行下一條命令
UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1);
}
//處理下一條Lock Record
most_recent++;
}
// Need to throw illegal monitor state exception
CALL_VM(InterpreterRuntime::throw_illegal_monitor_state_exception(THREAD), handle_exception);
ShouldNotReachHere();
}
輕量級(jí)鎖釋放時(shí)需要將Displaced Mark Word
替換到對(duì)象頭的mark word
中晌端。如果CAS失敗或者是重量級(jí)鎖則進(jìn)入到InterpreterRuntime::monitorexit
方法中。
//%note monitor_1
IRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorexit(JavaThread* thread, BasicObjectLock* elem))
Handle h_obj(thread, elem->obj());
...
ObjectSynchronizer::slow_exit(h_obj(), elem->lock(), thread);
// Free entry. This must be done here, since a pending exception might be installed on
//釋放Lock Record
elem->set_obj(NULL);
...
IRT_END
monitorexit
調(diào)用完slow_exit
方法后,就釋放Lock Record
恬砂。
void ObjectSynchronizer::slow_exit(oop object, BasicLock* lock, TRAPS) {
fast_exit (object, lock, THREAD) ;
}
void ObjectSynchronizer::fast_exit(oop object, BasicLock* lock, TRAPS) {
...
markOop dhw = lock->displaced_header();
markOop mark ;
if (dhw == NULL) {
// 重入鎖咧纠,什么也不做
...
return ;
}
mark = object->mark() ;
// 如果是mark word==Displaced Mark Word即輕量級(jí)鎖,CAS替換對(duì)象頭的mark word
if (mark == (markOop) lock) {
assert (dhw->is_neutral(), "invariant") ;
if ((markOop) Atomic::cmpxchg_ptr (dhw, object->mark_addr(), mark) == mark) {
TEVENT (fast_exit: release stacklock) ;
return;
}
}
//走到這里說(shuō)明是重量級(jí)鎖或者解鎖時(shí)發(fā)生了競(jìng)爭(zhēng)泻骤,膨脹后調(diào)用重量級(jí)鎖的exit方法漆羔。
ObjectSynchronizer::inflate(THREAD, object)->exit (true, THREAD) ;
}
該方法中先判斷是不是輕量級(jí)鎖,如果是輕量級(jí)鎖則將替換mark word
狱掂,否則膨脹為重量級(jí)鎖并調(diào)用exit
方法钧椰,相關(guān)邏輯將在重量級(jí)鎖的文章中講解。