本文為死磕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é)一些幫助。
本文分為兩個(gè)部分:
1.輕量級(jí)鎖獲取流程
2.輕量級(jí)鎖釋放流程
本人看的JVM版本是jdk8u。
下面開(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è)置為指向該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); }
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í)鎖的文章中講解。
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無(wú)理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性?xún)r(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專(zhuān)為企業(yè)上云打造定制,能夠滿(mǎn)足用戶(hù)豐富、多元化的應(yīng)用場(chǎng)景需求。
當(dāng)前文章:一文讓你讀懂Synchronized底層實(shí)現(xiàn),秒殺面試官-創(chuàng)新互聯(lián)
本文來(lái)源:http://jinyejixie.com/article44/digjee.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站排名、搜索引擎優(yōu)化、虛擬主機(jī)、Google、網(wǎng)站制作、服務(wù)器托管
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容