溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

死磕Synchronized底層實(shí)現(xiàn)--重量級鎖

發(fā)布時(shí)間:2020-08-11 04:17:21 來源:網(wǎng)絡(luò) 閱讀:4476 作者:Java筆記丶 欄目:編程語言

本文為死磕Synchronized底層實(shí)現(xiàn)第三篇文章,內(nèi)容為重量級鎖實(shí)現(xiàn)。

本系列文章將對HotSpot的synchronized鎖實(shí)現(xiàn)進(jìn)行全面分析,內(nèi)容包括偏向鎖、輕量級鎖、重量級鎖的加鎖、解鎖、鎖升級流程的原理及源碼分析,希望給在研究synchronized路上的同學(xué)一些幫助。

?

重量級的膨脹和加鎖流程

當(dāng)出現(xiàn)多個(gè)線程同時(shí)競爭鎖時(shí),會進(jìn)入到synchronizer.cpp#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");
??//?如果是無鎖狀態(tài)
??if?(mark->is_neutral())?{
????lock->set_displaced_header(mark);
????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;
??}

?...
?

??//?這時(shí)候需要膨脹為重量級鎖,膨脹前,設(shè)置Displaced?Mark?Word為一個(gè)特殊值,代表該鎖正在用一個(gè)重量級鎖的monitor
??lock->set_displaced_header(markOopDesc::unused_mark());
??//先調(diào)用inflate膨脹為重量級鎖,該方法返回一個(gè)ObjectMonitor對象,然后調(diào)用其enter方法
??ObjectSynchronizer::inflate(THREAD,?obj())->enter(THREAD);
}

inflate中完成膨脹過程。

ObjectMonitor?*?ATTR?ObjectSynchronizer::inflate?(Thread?*?Self,?oop?object)?{
??...

??for?(;;)?{
??????const?markOop?mark?=?object->mark()?;
??????assert?(!mark->has_bias_pattern(),?"invariant")?;
????
??????//?mark是以下狀態(tài)中的一種:
??????//?*??Inflated(重量級鎖狀態(tài))?????-?直接返回
??????//?*??Stack-locked(輕量級鎖狀態(tài))?-?膨脹
??????//?*??INFLATING(膨脹中)????-?忙等待直到膨脹完成
??????//?*??Neutral(無鎖狀態(tài))??????-?膨脹
??????//?*??BIASED(偏向鎖)???????-?非法狀態(tài),在這里不會出現(xiàn)

??????//?CASE:?inflated
??????if?(mark->has_monitor())?{
??????????//?已經(jīng)是重量級鎖狀態(tài)了,直接返回
??????????ObjectMonitor?*?inf?=?mark->monitor()?;
??????????...
??????????return?inf?;
??????}

??????//?CASE:?inflation?in?progress
??????if?(mark?==?markOopDesc::INFLATING())?{
?????????//?正在膨脹中,說明另一個(gè)線程正在進(jìn)行鎖膨脹,continue重試
?????????TEVENT?(Inflate:?spin?while?INFLATING)?;
?????????//?在該方法中會進(jìn)行spin/yield/park等操作完成自旋動作?
?????????ReadStableMark(object)?;
?????????continue?;
??????}
?
??????if?(mark->has_locker())?{
??????????//?當(dāng)前輕量級鎖狀態(tài),先分配一個(gè)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設(shè)置為INFLATING?(0)狀態(tài)?
??????????markOop?cmp?=?(markOop)?Atomic::cmpxchg_ptr?(markOopDesc::INFLATING(),?object->mark_addr(),?mark)?;
??????????if?(cmp?!=?mark)?{
?????????????omRelease?(Self,?m,?true)?;
?????????????continue?;???????//?Interference?--?just?retry
??????????}

??????????//?棧中的displaced?mark?word
??????????markOop?dmw?=?mark->displaced_mark_helper()?;
??????????assert?(dmw->is_neutral(),?"invariant")?;

??????????//?設(shè)置monitor的字段
??????????m->set_header(dmw)?;
??????????//?owner為Lock?Record
??????????m->set_owner(mark->locker());
??????????m->set_object(object);
??????????...
??????????//?將鎖對象頭設(shè)置為重量級鎖狀態(tài)
??????????object->release_set_mark(markOopDesc::encode(m));

?????????...
??????????return?m?;
??????}

??????//?CASE:?neutral
??	?
??????//?分配以及初始化ObjectMonitor對象
??????ObjectMonitor?*?m?=?omAlloc?(Self)?;
??????//?prepare?m?for?installation?-?set?monitor?to?initial?state
??????m->Recycle();
??????m->set_header(mark);
??????//?owner為NULL
??????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
	??//?用CAS替換對象頭的mark?word為重量級鎖狀態(tài)
??????if?(Atomic::cmpxchg_ptr?(markOopDesc::encode(m),?object->mark_addr(),?mark)?!=?mark)?{
??????????//?不成功說明有另外一個(gè)線程在執(zhí)行inflate,釋放monitor對象
??????????m->set_object?(NULL)?;
??????????m->set_owner??(NULL)?;
??????????m->OwnerIsThread?=?0?;
??????????m->Recycle()?;
??????????omRelease?(Self,?m,?true)?;
??????????m?=?NULL?;
??????????continue?;
??????????//?interference?-?the?markword?changed?-?just?retry.
??????????//?The?state-transitions?are?one-way,?so?there's?no?chance?of
??????????//?live-lock?--?"Inflated"?is?an?absorbing?state.
??????}

??????...
??????return?m?;
??}
}

inflate中是一個(gè)for循環(huán),主要是為了處理多線程同時(shí)調(diào)用inflate的情況。然后會根據(jù)鎖對象的狀態(tài)進(jìn)行不同的處理:

1.已經(jīng)是重量級狀態(tài),說明膨脹已經(jīng)完成,直接返回

2.如果是輕量級鎖則需要進(jìn)行膨脹操作

3.如果是膨脹中狀態(tài),則進(jìn)行忙等待

4.如果是無鎖狀態(tài)則需要進(jìn)行膨脹操作

其中輕量級鎖和無鎖狀態(tài)需要進(jìn)行膨脹操作,輕量級鎖膨脹流程如下:

1.調(diào)用omAlloc分配一個(gè)ObjectMonitor對象(以下簡稱monitor),在omAlloc方法中會先從線程私有的monitor集合omFreeList中分配對象,如果omFreeList中已經(jīng)沒有monitor對象,則從JVM全局的gFreeList中分配一批monitoromFreeList中。

2.初始化monitor對象

3.將狀態(tài)設(shè)置為膨脹中(INFLATING)狀態(tài)

4.設(shè)置monitor的header字段為displaced mark word,owner字段為Lock Record,obj字段為鎖對象

5.設(shè)置鎖對象頭的mark word為重量級鎖狀態(tài),指向第一步分配的monitor對象

無鎖狀態(tài)下的膨脹流程如下:

1.調(diào)用omAlloc分配一個(gè)ObjectMonitor對象(以下簡稱monitor)

2.初始化monitor對象

3.設(shè)置monitor的header字段為?mark word,owner字段為null,obj字段為鎖對象

4.設(shè)置鎖對象頭的mark word為重量級鎖狀態(tài),指向第一步分配的monitor對象

至于為什么輕量級鎖需要一個(gè)膨脹中(INFLATING)狀態(tài),代碼中的注釋是:

//?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.

我沒太看懂,有知道的同學(xué)可以指點(diǎn)下~

膨脹完成之后,會調(diào)用enter方法獲得鎖

void?ATTR?ObjectMonitor::enter(TRAPS)?{
???
??Thread?*?const?Self?=?THREAD?;
??void?*?cur?;
??//?owner為null代表無鎖狀態(tài),如果能CAS設(shè)置成功,則當(dāng)前線程直接獲得鎖
??cur?=?Atomic::cmpxchg_ptr?(Self,?&_owner,?NULL)?;
??if?(cur?==?NULL)?{
?????...
?????return?;
??}
??//?如果是重入的情況
??if?(cur?==?Self)?{
?????//?TODO-FIXME:?check?for?integer?overflow!??BUGID?6557169.
?????_recursions?++?;
?????return?;
??}
??//?當(dāng)前線程是之前持有輕量級鎖的線程。由輕量級鎖膨脹且第一次調(diào)用enter方法,那cur是指向Lock?Record的指針
??if?(Self->is_lock_owned?((address)cur))?{
????assert?(_recursions?==?0,?"internal?state?error");
????//?重入計(jì)數(shù)重置為1
????_recursions?=?1?;
????//?設(shè)置owner字段為當(dāng)前線程(之前owner是指向Lock?Record的指針)
????_owner?=?Self?;
????OwnerIsThread?=?1?;
????return?;
??}

??...

??//?在調(diào)用系統(tǒng)的同步操作之前,先嘗試自旋獲得鎖
??if?(Knob_SpinEarly?&&?TrySpin?(Self)?>?0)?{
?????...
?????//自旋的過程中獲得了鎖,則直接返回
?????Self->_Stalled?=?0?;
?????return?;
??}

??...

??{?
????...

????for?(;;)?{
??????jt->set_suspend_equivalent();
??????//?在該方法中調(diào)用系統(tǒng)同步操作
??????EnterI?(THREAD)?;
??????...
????}
????Self->set_current_pending_monitor(NULL);
????
??}

??...

}
  1. 如果當(dāng)前是無鎖狀態(tài)、鎖重入、當(dāng)前線程是之前持有輕量級鎖的線程則進(jìn)行簡單操作后返回。

  2. 先自旋嘗試獲得鎖,這樣做的目的是為了減少執(zhí)行操作系統(tǒng)同步操作帶來的開銷

  3. 調(diào)用EnterI方法獲得鎖或阻塞

EnterI方法比較長,在看之前,我們先闡述下其大致原理:

一個(gè)ObjectMonitor對象包括這么幾個(gè)關(guān)鍵字段:cxq(下圖中的ContentionList),EntryList ,WaitSet,owner。

其中cxq ,EntryList ,WaitSet都是由ObjectWaiter的鏈表結(jié)構(gòu),owner指向持有鎖的線程。

死磕Synchronized底層實(shí)現(xiàn)--重量級鎖

當(dāng)一個(gè)線程嘗試獲得鎖時(shí),如果該鎖已經(jīng)被占用,則會將該線程封裝成一個(gè)ObjectWaiter對象插入到cxq的隊(duì)列的隊(duì)首,然后調(diào)用park函數(shù)掛起當(dāng)前線程。在linux系統(tǒng)上,park函數(shù)底層調(diào)用的是gclib庫的pthread_cond_wait,JDK的ReentrantLock底層也是用該方法掛起線程的。更多細(xì)節(jié)可以看我之前的兩篇文章:關(guān)于同步的一點(diǎn)思考-下,linux內(nèi)核級同步機(jī)制–futex

當(dāng)線程釋放鎖時(shí),會從cxq或EntryList中挑選一個(gè)線程喚醒,被選中的線程叫做Heir presumptive即假定繼承人(應(yīng)該是這樣翻譯),就是圖中的Ready Thread,假定繼承人被喚醒后會嘗試獲得鎖,但synchronized是非公平的,所以假定繼承人不一定能獲得鎖(這也是它叫”假定”繼承人的原因)。

如果線程獲得鎖后調(diào)用Object#wait方法,則會將線程加入到WaitSet中,當(dāng)被Object#notify喚醒后,會將線程從WaitSet移動到cxq或EntryList中去。需要注意的是,當(dāng)調(diào)用一個(gè)鎖對象的waitnotify方法時(shí),如當(dāng)前鎖的狀態(tài)是偏向鎖或輕量級鎖則會先膨脹成重量級鎖。

synchronizedmonitor鎖機(jī)制和JDK的ReentrantLockCondition是很相似的,ReentrantLock也有一個(gè)存放等待獲取鎖線程的鏈表,Condition也有一個(gè)類似WaitSet的集合用來存放調(diào)用了await的線程。如果你之前對ReentrantLock有深入了解,那理解起monitor應(yīng)該是很簡單。

回到代碼上,開始分析EnterI方法:

void?ATTR?ObjectMonitor::EnterI?(TRAPS)?{
????Thread?*?Self?=?THREAD?;
????...
????//?嘗試獲得鎖
????if?(TryLock?(Self)?>?0)?{
????????...
????????return?;
????}

????DeferredInitialize?()?;
?
	//?自旋
????if?(TrySpin?(Self)?>?0)?{
????????...
????????return?;
????}
????
????...
	
????//?將線程封裝成node節(jié)點(diǎn)中
????ObjectWaiter?node(Self)?;
????Self->_ParkEvent->reset()?;
????node._prev???=?(ObjectWaiter?*)?0xBAD?;
????node.TState??=?ObjectWaiter::TS_CXQ?;

????//?將node節(jié)點(diǎn)插入到_cxq隊(duì)列的頭部,cxq是一個(gè)單向鏈表
????ObjectWaiter?*?nxt?;
????for?(;;)?{
????????node._next?=?nxt?=?_cxq?;
????????if?(Atomic::cmpxchg_ptr?(&node,?&_cxq,?nxt)?==?nxt)?break?;

????????//?CAS失敗的話?再嘗試獲得鎖,這樣可以降低插入到_cxq隊(duì)列的頻率
????????if?(TryLock?(Self)?>?0)?{
????????????...
????????????return?;
????????}
????}

	//?SyncFlags默認(rèn)為0,如果沒有其他等待的線程,則將_Responsible設(shè)置為自己
????if?((SyncFlags?&?16)?==?0?&&?nxt?==?NULL?&&?_EntryList?==?NULL)?{
????????Atomic::cmpxchg_ptr?(Self,?&_Responsible,?NULL)?;
????}


????TEVENT?(Inflated?enter?-?Contention)?;
????int?nWakeups?=?0?;
????int?RecheckInterval?=?1?;

????for?(;;)?{

????????if?(TryLock?(Self)?>?0)?break?;
????????assert?(_owner?!=?Self,?"invariant")?;

????????...

????????//?park?self
????????if?(_Responsible?==?Self?||?(SyncFlags?&?1))?{
????????????//?當(dāng)前線程是_Responsible時(shí),調(diào)用的是帶時(shí)間參數(shù)的park
????????????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?{
????????????//否則直接調(diào)用park掛起當(dāng)前線程
????????????TEVENT?(Inflated?enter?-?park?UNTIMED)?;
????????????Self->_ParkEvent->park()?;
????????}

????????if?(TryLock(Self)?>?0)?break?;

????????...
????????
????????if?((Knob_SpinAfterFutile?&?1)?&&?TrySpin?(Self)?>?0)?break?;

???????	...
????????//?在釋放鎖時(shí),_succ會被設(shè)置為EntryList或_cxq中的一個(gè)線程
????????if?(_succ?==?Self)?_succ?=?NULL?;

????????//?Invariant:?after?clearing?_succ?a?thread?*must*?retry?_owner?before?parking.
????????OrderAccess::fence()?;
????}

???//?走到這里說明已經(jīng)獲得鎖了

????assert?(_owner?==?Self??????,?"invariant")?;
????assert?(object()?!=?NULL????,?"invariant")?;
??
	//?將當(dāng)前線程的node從cxq或EntryList中移除
????UnlinkAfterAcquire?(Self,?&node)?;
????if?(_succ?==?Self)?_succ?=?NULL?;
	if?(_Responsible?==?Self)?{
????????_Responsible?=?NULL?;
????????OrderAccess::fence();
????}
????...
????return?;
}

主要步驟有3步:

  1. 將當(dāng)前線程插入到cxq隊(duì)列的隊(duì)首

  2. 然后park當(dāng)前線程

  3. 當(dāng)被喚醒后再嘗試獲得鎖

這里需要特別說明的是_Responsible_succ兩個(gè)字段的作用:

當(dāng)競爭發(fā)生時(shí),選取一個(gè)線程作為_Responsible,_Responsible線程調(diào)用的是有時(shí)間限制的park方法,其目的是防止出現(xiàn)擱淺現(xiàn)象。

_succ線程是在線程釋放鎖是被設(shè)置,其含義是Heir presumptive,也就是我們上面說的假定繼承人。

重量級鎖的釋放

重量級鎖釋放的代碼在ObjectMonitor::exit

void?ATTR?ObjectMonitor::exit(bool?not_suspended,?TRAPS)?{
???Thread?*?Self?=?THREAD?;
???//?如果_owner不是當(dāng)前線程
???if?(THREAD?!=?_owner)?{
?????//?當(dāng)前線程是之前持有輕量級鎖的線程。由輕量級鎖膨脹后還沒調(diào)用過enter方法,_owner會是指向Lock?Record的指針。
?????if?(THREAD->is_lock_owned((address)?_owner))?{
???????assert?(_recursions?==?0,?"invariant")?;
???????_owner?=?THREAD?;
???????_recursions?=?0?;
???????OwnerIsThread?=?1?;
?????}?else?{
???????//?異常情況:當(dāng)前不是持有鎖的線程
???????TEVENT?(Exit?-?Throw?IMSX)?;
???????assert(false,?"Non-balanced?monitor?enter/exit!");
???????if?(false)?{
??????????THROW(vmSymbols::java_lang_IllegalMonitorStateException());
???????}
???????return;
?????}
???}
???//?重入計(jì)數(shù)器還不為0,則計(jì)數(shù)器-1后返回
???if?(_recursions?!=?0)?{
?????_recursions--;????????//?this?is?simple?recursive?enter
?????TEVENT?(Inflated?exit?-?recursive)?;
?????return?;
???}

???//?_Responsible設(shè)置為null
???if?((SyncFlags?&?4)?==?0)?{
??????_Responsible?=?NULL?;
???}

???...

???for?(;;)?{
??????assert?(THREAD?==?_owner,?"invariant")?;

??????//?Knob_ExitPolicy默認(rèn)為0
??????if?(Knob_ExitPolicy?==?0)?{
?????????//?code?1:先釋放鎖,這時(shí)如果有其他線程進(jìn)入同步塊則能獲得鎖
?????????OrderAccess::release_store_ptr?(&_owner,?NULL)?;???//?drop?the?lock
?????????OrderAccess::storeload()?;?????????????????????????//?See?if?we?need?to?wake?a?successor
?????????//?code?2:如果沒有等待的線程或已經(jīng)有假定繼承人
?????????if?((intptr_t(_EntryList)|intptr_t(_cxq))?==?0?||?_succ?!=?NULL)?{
????????????TEVENT?(Inflated?exit?-?simple?egress)?;
????????????return?;
?????????}
?????????TEVENT?(Inflated?exit?-?complex?egress)?;

?????????//?code?3:要執(zhí)行之后的操作需要重新獲得鎖,即設(shè)置_owner為當(dāng)前線程
?????????if?(Atomic::cmpxchg_ptr?(THREAD,?&_owner,?NULL)?!=?NULL)?{
????????????return?;
?????????}
?????????TEVENT?(Exit?-?Reacquired)?;
??????}?
??????...

??????ObjectWaiter?*?w?=?NULL?;
??????//?code?4:根據(jù)QMode的不同會有不同的喚醒策略,默認(rèn)為0
??????int?QMode?=?Knob_QMode?;
	?
??????if?(QMode?==?2?&&?_cxq?!=?NULL)?{
??????????//?QMode?==?2?:?cxq中的線程有更高優(yōu)先級,直接喚醒cxq的隊(duì)首線程
??????????w?=?_cxq?;
??????????assert?(w?!=?NULL,?"invariant")?;
??????????assert?(w->TState?==?ObjectWaiter::TS_CXQ,?"Invariant")?;
??????????ExitEpilog?(Self,?w)?;
??????????return?;
??????}

??????if?(QMode?==?3?&&?_cxq?!=?NULL)?{
??????????//?將cxq中的元素插入到EntryList的末尾
??????????w?=?_cxq?;
??????????for?(;;)?{
?????????????assert?(w?!=?NULL,?"Invariant")?;
?????????????ObjectWaiter?*?u?=?(ObjectWaiter?*)?Atomic::cmpxchg_ptr?(NULL,?&_cxq,?w)?;
?????????????if?(u?==?w)?break?;
?????????????w?=?u?;
??????????}
??????????assert?(w?!=?NULL??????????????,?"invariant")?;

??????????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?;
??????????}

??????????//?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
??????}

??????if?(QMode?==?4?&&?_cxq?!=?NULL)?{
??????????//?將cxq插入到EntryList的隊(duì)首
??????????w?=?_cxq?;
??????????for?(;;)?{
?????????????assert?(w?!=?NULL,?"Invariant")?;
?????????????ObjectWaiter?*?u?=?(ObjectWaiter?*)?Atomic::cmpxchg_ptr?(NULL,?&_cxq,?w)?;
?????????????if?(u?==?w)?break?;
?????????????w?=?u?;
??????????}
??????????assert?(w?!=?NULL??????????????,?"invariant")?;

??????????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?;
??????????}

??????????//?Prepend?the?RATs?to?the?EntryList
??????????if?(_EntryList?!=?NULL)?{
??????????????q->_next?=?_EntryList?;
??????????????_EntryList->_prev?=?q?;
??????????}
??????????_EntryList?=?w?;

??????????//?Fall?thru?into?code?that?tries?to?wake?a?successor?from?EntryList
??????}

??????w?=?_EntryList??;
??????if?(w?!=?NULL)?{
??????????//?如果EntryList不為空,則直接喚醒EntryList的隊(duì)首元素
??????????assert?(w->TState?==?ObjectWaiter::TS_ENTER,?"invariant")?;
??????????ExitEpilog?(Self,?w)?;
??????????return?;
??????}

??????//?EntryList為null,則處理cxq中的元素
??????w?=?_cxq?;
??????if?(w?==?NULL)?continue?;

??????//?因?yàn)橹笠獙xq的元素移動到EntryList,所以這里將cxq字段設(shè)置為null
??????for?(;;)?{
??????????assert?(w?!=?NULL,?"Invariant")?;
??????????ObjectWaiter?*?u?=?(ObjectWaiter?*)?Atomic::cmpxchg_ptr?(NULL,?&_cxq,?w)?;
??????????if?(u?==?w)?break?;
??????????w?=?u?;
??????}
??????TEVENT?(Inflated?exit?-?drain?cxq?into?EntryList)?;

??????assert?(w?!=?NULL??????????????,?"invariant")?;
??????assert?(_EntryList??==?NULL????,?"invariant")?;


??????if?(QMode?==?1)?{
?????????//?QMode?==?1?:?將cxq中的元素轉(zhuǎn)移到EntryList,并反轉(zhuǎn)順序
?????????ObjectWaiter?*?s?=?NULL?;
?????????ObjectWaiter?*?t?=?w?;
?????????ObjectWaiter?*?u?=?NULL?;
?????????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??=?s?;
?????????assert?(s?!=?NULL,?"invariant")?;
??????}?else?{
?????????//?QMode?==?0?or?QMode?==?2‘
?????????//?將cxq中的元素轉(zhuǎn)移到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?;
?????????}
??????}


??????//?_succ不為null,說明已經(jīng)有個(gè)繼承人了,所以不需要當(dāng)前線程去喚醒,減少上下文切換的比率
??????if?(_succ?!=?NULL)?continue;

??????w?=?_EntryList??;
??????//?喚醒EntryList第一個(gè)元素
??????if?(w?!=?NULL)?{
??????????guarantee?(w->TState?==?ObjectWaiter::TS_ENTER,?"invariant")?;
??????????ExitEpilog?(Self,?w)?;
??????????return?;
??????}
???}
}

在進(jìn)行必要的鎖重入判斷以及自旋優(yōu)化后,進(jìn)入到主要邏輯:

code 1?設(shè)置owner為null,即釋放鎖,這個(gè)時(shí)刻其他的線程能獲取到鎖。這里是一個(gè)非公平鎖的優(yōu)化;

code 2?如果當(dāng)前沒有等待的線程則直接返回就好了,因?yàn)椴恍枰獑拘哑渌€程?;蛘呷绻fsucc不為null,代表當(dāng)前已經(jīng)有個(gè)”醒著的”繼承人線程,那當(dāng)前線程不需要喚醒任何線程;

code 3?當(dāng)前線程重新獲得鎖,因?yàn)橹笠僮鱟xq和EntryList隊(duì)列以及喚醒線程;

code 4根據(jù)QMode的不同,會執(zhí)行不同的喚醒策略;

根據(jù)QMode的不同,有不同的處理方式:

  1. QMode = 2且cxq非空:取cxq隊(duì)列隊(duì)首的ObjectWaiter對象,調(diào)用ExitEpilog方法,該方法會喚醒ObjectWaiter對象的線程,然后立即返回,后面的代碼不會執(zhí)行了;

  2. QMode = 3且cxq非空:把cxq隊(duì)列插入到EntryList的尾部;

  3. QMode = 4且cxq非空:把cxq隊(duì)列插入到EntryList的頭部;

  4. QMode = 0:暫時(shí)什么都不做,繼續(xù)往下看;

只有QMode=2的時(shí)候會提前返回,等于0、3、4的時(shí)候都會繼續(xù)往下執(zhí)行:

1.如果EntryList的首元素非空,就取出來調(diào)用ExitEpilog方法,該方法會喚醒ObjectWaiter對象的線程,然后立即返回;
2.如果EntryList的首元素為空,就將cxq的所有元素放入到EntryList中,然后再從EntryList中取出來隊(duì)首元素執(zhí)行ExitEpilog方法,然后立即返回;

QMode默認(rèn)為0,結(jié)合上面的流程我們可以看這么個(gè)demo:

public?class?SyncDemo?{

????public?static?void?main(String[]?args)?{

????????SyncDemo?syncDemo1?=?new?SyncDemo();
????????syncDemo1.startThreadA();
????????try?{
????????????Thread.sleep(100);
????????}?catch?(InterruptedException?e)?{
????????????e.printStackTrace();
????????}
????????syncDemo1.startThreadB();
????????try?{
????????????Thread.sleep(100);
????????}?catch?(InterruptedException?e)?{
????????????e.printStackTrace();
????????}
????????syncDemo1.startThreadC();
???????

????}

????final?Object?lock?=?new?Object();


????public?void?startThreadA()?{
????????new?Thread(()?->?{
????????????synchronized?(lock)?{
????????????????System.out.println("A?get?lock");
????????????????try?{
????????????????????Thread.sleep(500);
????????????????}?catch?(InterruptedException?e)?{
????????????????????e.printStackTrace();
????????????????}
????????????????System.out.println("A?release?lock");
????????????}
????????},?"thread-A").start();
????}

????public?void?startThreadB()?{
????????new?Thread(()?->?{
????????????synchronized?(lock)?{
????????????????System.out.println("B?get?lock");
????????????}
????????},?"thread-B").start();
????}

????public?void?startThreadC()?{
????????new?Thread(()?->?{
????????????synchronized?(lock)?{

????????????????System.out.println("C?get?lock");
????????????}
????????},?"thread-C").start();
????}


}

默認(rèn)策略下,在A釋放鎖后一定是C線程先獲得鎖。因?yàn)樵讷@取鎖時(shí),是將當(dāng)前線程插入到cxq的頭部,而釋放鎖時(shí),默認(rèn)策略是:如果EntryList為空,則將cxq中的元素按原有順序插入到到EntryList,并喚醒第一個(gè)線程。也就是當(dāng)EntryList為空時(shí),是后來的線程先獲取鎖。這點(diǎn)JDK中的Lock機(jī)制是不一樣的。

?

Synchronized和ReentrantLock的區(qū)別

原理弄清楚了,順便總結(jié)了幾點(diǎn)Synchronized和ReentrantLock的區(qū)別:

  1. Synchronized是JVM層次的鎖實(shí)現(xiàn),ReentrantLock是JDK層次的鎖實(shí)現(xiàn);

  2. Synchronized的鎖狀態(tài)是無法在代碼中直接判斷的,但是ReentrantLock可以通過ReentrantLock#isLocked判斷;

  3. Synchronized是非公平鎖,ReentrantLock是可以是公平也可以是非公平的;

  4. Synchronized是不可以被中斷的,而ReentrantLock#lockInterruptibly方法是可以被中斷的;

  5. 在發(fā)生異常時(shí)Synchronized會自動釋放鎖(由javac編譯時(shí)自動實(shí)現(xiàn)),而ReentrantLock需要開發(fā)者在finally塊中顯示釋放鎖;

  6. ReentrantLock獲取鎖的形式有多種:如立即返回是否成功的tryLock(),以及等待指定時(shí)長的獲取,更加靈活;

  7. Synchronized在特定的情況下對于已經(jīng)在等待的線程是后來的線程先獲得鎖(上文有說),而ReentrantLock對于已經(jīng)在等待的線程一定是先來的線程先獲得鎖;

?

End

總的來說Synchronized的重量級鎖和ReentrantLock的實(shí)現(xiàn)上還是有很多相似的,包括其數(shù)據(jù)結(jié)構(gòu)、掛起線程方式等等。在日常使用中,如無特殊要求用Synchronized就夠了。你深入了解這兩者其中一個(gè)的實(shí)現(xiàn),了解另外一個(gè)或其他鎖機(jī)制都比較容易,這也是我們常說的技術(shù)上的相通性。


向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI