一文帶你掌握J(rèn)ava?ReentrantLock加解鎖原理
簡(jiǎn)要總結(jié) ReentrantLock
實(shí)現(xiàn)原理:volatile 變量 + CAS設(shè)置值 + AQS + 兩個(gè)隊(duì)列
實(shí)現(xiàn)阻塞:同步隊(duì)列 + CAS搶占標(biāo)記為 valatile 的 state
實(shí)現(xiàn)等待喚醒:await :持有鎖,park ->加入等待隊(duì)列 ;signal:?jiǎn)拘严乱粋€(gè)等待隊(duì)列節(jié)點(diǎn),轉(zhuǎn)移進(jìn)入同步隊(duì)列,然后CAS搶占或者按照阻塞隊(duì)列等待搶占。接著 await 后續(xù)內(nèi)容程序得以繼續(xù)執(zhí)行。
ReentrantLock 結(jié)構(gòu)分析
ReentrantLock 繼承了Lock接口, lock方法實(shí)際上是調(diào)用了Sync的子類NonfairSync(非公平鎖)的lock方法。ReentrantLock的真正實(shí)現(xiàn)在他的兩個(gè)內(nèi)部類NonfairSync 和 FairSync中,默認(rèn)實(shí)現(xiàn)是非公平鎖。并且內(nèi)部類都繼承于內(nèi)部類Sync,而Sync根本的實(shí)現(xiàn)則是大名鼎鼎的 AbstractQueuedSynchronizer
同步器(AQS)。
具體詳見(jiàn)如下代碼:
public?class?ReentrantLock?implements?Lock,?java.io.Serializable?{ ????private?static?final?long?serialVersionUID?=?7373984872572414699L; ????/**?Synchronizer?providing?all?implementation?mechanics?*/ ????private?final?Sync?sync; ?? ??public?ReentrantLock()?{ ????????sync?=?new?NonfairSync(); ????} ?????abstract?static?class?Sync?extends?AbstractQueuedSynchronizer?{ ???????……省略代碼 ?????} ?? ??//非公平鎖 ??static?final?class?NonfairSync?extends?Sync?{ ????????private?static?final?long?serialVersionUID?=?7316153563782823691L; ????????/** ?????????*?Performs?lock.??Try?immediate?barge,?backing?up?to?normal ?????????*?acquire?on?failure. ?????????*/ ????????final?void?lock()?{ ????????????if?(compareAndSetState(0,?1)) ????????????????setExclusiveOwnerThread(Thread.currentThread()); ????????????else ????????????????acquire(1); ????????} ????????protected?final?boolean?tryAcquire(int?acquires)?{ ????????????return?nonfairTryAcquire(acquires); ????????} ????} ??//?公平鎖 ??static?final?class?FairSync?extends?Sync?{ ????????private?static?final?long?serialVersionUID?=?-3000897897090466540L; ????????final?void?lock()?{ ????????????acquire(1); ????????} ????????/** ?????????*?Fair?version?of?tryAcquire.??Don't?grant?access?unless ?????????*?recursive?call?or?no?waiters?or?is?first. ?????????*/ ????????protected?final?boolean?tryAcquire(int?acquires)?{ ……省略 ????????} ????} ??//?lock?方法本質(zhì)就是調(diào)用sync類 ??public?void?lock()?{ ????????sync.lock(); ????} }
lock 加鎖過(guò)程
按照調(diào)用 lock
方法是否搶占鎖成功,可以以調(diào)用 park 方法為界限,將加鎖的過(guò)程分為兩部分:一部分是當(dāng)前線程被阻塞前,另一部分是線程被喚醒繼續(xù)執(zhí)行后。(這里以非公平鎖為例)
阻塞前
1.直接通過(guò)CAS嘗試獲取鎖,設(shè)置state為1。如果獲取成功則將鎖標(biāo)識(shí)設(shè)為獨(dú)占,就是是將當(dāng)前線程設(shè)置給 exclusiveOwnerThread。
final?void?lock()?{ ????if?(compareAndSetState(0,?1)) ????????setExclusiveOwnerThread(Thread.currentThread()); ????else ????????acquire(1); }
2.如果獲取失敗,再次嘗試獲取,調(diào)用acquire。
3.tryAcquire ->:判斷鎖是否被占有,如果空閑則再次嘗試CAS獲取鎖;如果已被占有則對(duì)比占有鎖的線程是否為本線程,是的話將state+1,這就是可重入鎖的關(guān)鍵邏輯。
//AbstractQueuedSynchronizer public?final?void?acquire(int?arg)?{ ????if?(!tryAcquire(arg)?&& ????????acquireQueued(addWaiter(Node.EXCLUSIVE),?arg)) ????????selfInterrupt(); } //ReentrantLock.NonfairSync protected?final?boolean?tryAcquire(int?acquires)?{ ????return?nonfairTryAcquire(acquires); } //ReentrantLock.Sync final?boolean?nonfairTryAcquire(int?acquires)?{ ????final?Thread?current?=?Thread.currentThread(); ????int?c?=?getState(); ????if?(c?==?0)?{ ??????//?cas再次嘗試獲取 ????????if?(compareAndSetState(0,?acquires))?{ ????????????setExclusiveOwnerThread(current); ????????????return?true; ????????} ????} ????else?if?(current?==?getExclusiveOwnerThread())?{ ??????//?可重入邏輯 ????????int?nextc?=?c?+?acquires; ????????if?(nextc?<?0)?//?overflow ????????????throw?new?Error("Maximum?lock?count?exceeded"); ????????setState(nextc); ????????return?true; ????} ????return?false; }
4.如果獲取失敗則將節(jié)點(diǎn)插入隊(duì)列尾部,如果隊(duì)列為空,則會(huì)初始化隊(duì)列,并且設(shè)置頭尾節(jié)點(diǎn)為空節(jié)點(diǎn),再將Node設(shè)為尾節(jié)點(diǎn)。
//?獲取鎖失敗 acquireQueued(addWaiter(Node.EXCLUSIVE),?arg)) //?加入同步隊(duì)列 private?Node?addWaiter(Node?mode)?{ ????Node?node?=?new?Node(Thread.currentThread(),?mode); ????//?Try?the?fast?path?of?enq;?backup?to?full?enq?on?failure ????Node?pred?=?tail; ????if?(pred?!=?null)?{ ????????node.prev?=?pred; ??????//?通過(guò)CAS設(shè)置尾節(jié)點(diǎn)為當(dāng)前節(jié)點(diǎn),前驅(qū)節(jié)點(diǎn)為之前的尾節(jié)點(diǎn)。 ????????if?(compareAndSetTail(pred,?node))?{ ????????????pred.next?=?node; ????????????return?node; ????????} ????} ??//?如果當(dāng)前鏈表為空,則在此處進(jìn)行初始化 ????enq(node); ????return?node; } private?Node?enq(final?Node?node)?{ ????for?(;;)?{ ????????Node?t?=?tail; ????????if?(t?==?null)?{?//?Must?initialize ????????????if?(compareAndSetHead(new?Node())) ????????????????tail?=?head; ????????}?else?{ ??????????//?追加到隊(duì)列尾 ????????????node.prev?=?t; ????????????if?(compareAndSetTail(t,?node))?{ ????????????????t.next?=?node; ????????????????return?t; ????????????} ????????} ????} }
5.將新建的Node傳入acquireQueued
,獲取前驅(qū)節(jié)點(diǎn),如果節(jié)點(diǎn)就是head 頭節(jié)點(diǎn),那么嘗試CAS競(jìng)爭(zhēng)鎖(head隨時(shí)釋放)。如果搶占成功將頭節(jié)點(diǎn)設(shè)為自己。
final?boolean?acquireQueued(final?Node?node,?int?arg)?{ ????boolean?failed?=?true; ????try?{ ????????boolean?interrupted?=?false; ????????for?(;;)?{ ????????????final?Node?p?=?node.predecessor(); ??????????//?如果是頭節(jié)點(diǎn),再次嘗試 ????????????if?(p?==?head?&&?tryAcquire(arg))?{ ????????????????setHead(node); ????????????????p.next?=?null;?//?help?GC ????????????????failed?=?false; ????????????????return?interrupted; ????????????} ????????????if?(shouldParkAfterFailedAcquire(p,?node)?&& ????????????????parkAndCheckInterrupt()) ????????????????interrupted?=?true; ????????} ????}?finally?{ ????????if?(failed) ????????????cancelAcquire(node); ????} }
6.如果沒(méi)有搶占成功,則進(jìn)入shouldParkAfterFailedAcquire
邏輯,將前驅(qū)節(jié)點(diǎn)設(shè)置為Signal,表示后繼節(jié)點(diǎn)(也就是當(dāng)前節(jié)點(diǎn))需要前驅(qū)節(jié)點(diǎn)去喚醒。設(shè)置完之后再次進(jìn)入自旋鎖,嘗試獲得鎖。
關(guān)于Node的狀態(tài)這里說(shuō)明一下:
節(jié)點(diǎn)剛創(chuàng)建的時(shí)候,status=0,假設(shè)這時(shí)候本節(jié)點(diǎn)就是head節(jié)點(diǎn),那么他會(huì)進(jìn)入else邏輯,將自身狀態(tài)設(shè)置為Signal,然后再次進(jìn)入自旋,嘗試獲取鎖。如果還是沒(méi)有獲取到鎖,那么再次進(jìn)入shouldParkAfterFailedAcquire
方法后會(huì)進(jìn)入第一個(gè)if邏輯,方法返回True。
/** *?Checks?and?updates?status?for?a?node?that?failed?to?acquire. *?Returns?true?if?thread?should?block.?This?is?the?main?signal *?control?in?all?acquire?loops.??Requires?that?pred?==?node.prev. *?如果獲取鎖失敗,檢查并且更新節(jié)點(diǎn)。如果需要被park阻塞,返回true。 *?在所有的循環(huán)邏輯中,這是主要的信號(hào)控制邏輯。 * * pred:表示前驅(qū)節(jié)點(diǎn) * node:表示當(dāng)前線程節(jié)點(diǎn) */ private?static?boolean?shouldParkAfterFailedAcquire(Node?pred,?Node?node)?{ ????int?ws?=?pred.waitStatus; ????if?(ws?==?Node.SIGNAL) ???????//?第二次嘗試獲取鎖會(huì)進(jìn)入這段邏輯 ????????/* ?????????*?This?node?has?already?set?status?asking?a?release ?????????*?to?signal?it,?so?it?can?safely?park. ?????????*/ ?????????//?表明線程已經(jīng)準(zhǔn)備好被阻塞并等待之后被喚醒 ????????return?true; ????if?(ws?>?0)?{ ????????/* ?????????*?Predecessor?was?cancelled.?Skip?over?predecessors?and ?????????*?indicate?retry. ?????????*/ ?????????//?若pred.waitStatus狀態(tài)位大于0,說(shuō)明這個(gè)前驅(qū)點(diǎn)已經(jīng)取消了獲取鎖的操作, ?????????//?doWhile循環(huán)會(huì)遞歸刪除掉這些放棄獲取鎖的節(jié)點(diǎn) ????????do?{ ????????????node.prev?=?pred?=?pred.prev; ????????}?while?(pred.waitStatus?>?0); ????????pred.next?=?node; ????}?else?{ ????????/* ?????????*?節(jié)點(diǎn)剛創(chuàng)建的時(shí)候,status=0,邏輯會(huì)走到這里將自身狀態(tài)設(shè)置為signal ?????????*?waitStatus?must?be?0?or?PROPAGATE.??Indicate?that?we ?????????*?need?a?signal,?but?don't?park?yet.??Caller?will?need?to ?????????*?retry?to?make?sure?it?cannot?acquire?before?parking. ?????????*/ ?????????//若狀態(tài)位不為Node.SIGNAL,且沒(méi)有取消操作,則會(huì)嘗試將前驅(qū)節(jié)點(diǎn)狀態(tài)位修改為Node.SIGNAL ????????//?表示將會(huì)喚醒后繼節(jié)點(diǎn) ????????compareAndSetWaitStatus(pred,?ws,?Node.SIGNAL); ????} ????return?false; }
7.第二次自旋獲取失敗后,由于前驅(qū)節(jié)點(diǎn)已經(jīng)是Signal,這時(shí)進(jìn)入parkAndCheckInterrupt,將當(dāng)前線程阻塞,等待被喚醒。后續(xù)其他線程如果也嘗試搶占鎖,會(huì)同樣被阻塞。
private?final?boolean?parkAndCheckInterrupt()?{ ??//?阻塞線程 ????LockSupport.park(this); ??//?線程繼續(xù)執(zhí)行 ????return?Thread.interrupted(); }
park方法被喚醒后
在其他線程釋放鎖資源后,喚醒下一個(gè)節(jié)點(diǎn),park
的后半部分邏輯繼續(xù)執(zhí)行。
1.繼續(xù)執(zhí)行之前Park之后的邏輯,在此處線程被喚醒。這里會(huì)返回中斷標(biāo)記,這也是為什么ReentrantLock可以相應(yīng)中斷的原因。
2.然后再次進(jìn)入自旋鎖,使用CAS獲取到鎖標(biāo)記,將頭節(jié)點(diǎn)設(shè)為當(dāng)前節(jié)點(diǎn),然后返回中斷標(biāo)記跳出循環(huán)。
3.至此,獲取鎖流程結(jié)束。
unlock 釋放鎖過(guò)程
1.嘗試釋放鎖,用state減去1,判斷是否等于0。如果等于0表示已經(jīng)完全釋放鎖,將線程標(biāo)記設(shè)為null。否則釋放失敗,表示當(dāng)前線程仍在繼續(xù)持有,繼續(xù)持有說(shuō)明有重入情況。
//?ReentrantLock public?void?unlock()?{ ????sync.release(1); } //?AQS public?final?boolean?release(int?arg)?{ ??//?釋放鎖 ????if?(tryRelease(arg))?{ ????????Node?h?=?head; ????????if?(h?!=?null?&&?h.waitStatus?!=?0) ??????????//?喚醒后繼節(jié)點(diǎn) ????????????unparkSuccessor(h); ????????return?true; ????} ????return?false; } //?釋放鎖 protected?final?boolean?tryRelease(int?releases)?{ ????int?c?=?getState()?-?releases; ????if?(Thread.currentThread()?!=?getExclusiveOwnerThread()) ????????throw?new?IllegalMonitorStateException(); ????boolean?free?=?false; ????if?(c?==?0)?{ ????????free?=?true; ??????//?釋放鎖 ????????setExclusiveOwnerThread(null); ????} ????setState(c); ????return?free; }
2.拿到頭節(jié)點(diǎn),然后解鎖后繼節(jié)點(diǎn)。如果當(dāng)前節(jié)點(diǎn)狀態(tài)小于0(signal=-1),則修改節(jié)點(diǎn)status為0。然后向后遞歸找到status小于等于0的節(jié)點(diǎn)(正常為0),調(diào)用unpark解除阻塞。返回解鎖成功。
//?喚醒后繼節(jié)點(diǎn) private?void?unparkSuccessor(Node?node)?{ ????/* ?????*?If?status?is?negative?(i.e.,?possibly?needing?signal)?try ?????*?to?clear?in?anticipation?of?signalling.??It?is?OK?if?this ?????*?fails?or?if?status?is?changed?by?waiting?thread. ?????*/ ????int?ws?=?node.waitStatus; ????if?(ws?<?0) ????????compareAndSetWaitStatus(node,?ws,?0); ????/* ?????*?Thread?to?unpark?is?held?in?successor,?which?is?normally ?????*?just?the?next?node.??But?if?cancelled?or?apparently?null, ?????*?traverse?backwards?from?tail?to?find?the?actual ?????*?non-cancelled?successor. ?????*/ ??//?拿到下一個(gè)節(jié)點(diǎn) ????Node?s?=?node.next; ??//要解除阻塞的線程在后繼節(jié)點(diǎn)中,通常只是下一個(gè)節(jié)點(diǎn)。但如果取消或明顯為空,則從尾部向前遍歷以找到實(shí)際未取消的繼任者。 ????if?(s?==?null?||?s.waitStatus?>?0)?{ ????????s?=?null; ????????for?(Node?t?=?tail;?t?!=?null?&&?t?!=?node;?t?=?t.prev) ????????????if?(t.waitStatus?<=?0) ????????????????s?=?t; ????} ????if?(s?!=?null) ??????//解鎖 ????????LockSupport.unpark(s.thread); }
3.在這之后便繼續(xù)開(kāi)始執(zhí)行之前被阻塞的線程中的邏輯。
到這里 ReentrantLock 的加解鎖過(guò)程原理便講解結(jié)束,關(guān)于條件隊(duì)列的內(nèi)容,有興趣后續(xù)文章會(huì)做講解。
對(duì)比 Synchronized
既然已經(jīng)了解了 ReentrantLock ,那么在此對(duì)大家所熟知的 Synchronized 進(jìn)行一個(gè)對(duì)比。
與Synchronized相同點(diǎn)
1.ReentrantLock和synchronized都是獨(dú)占鎖,只允許線程互斥的訪問(wèn)臨界區(qū)。
但是實(shí)現(xiàn)上兩者不同:synchronized加鎖解鎖的過(guò)程是隱式的,用戶不用手動(dòng)操作,優(yōu)點(diǎn)是操作簡(jiǎn)單,但顯得不夠靈活。一般并發(fā)場(chǎng)景使用synchronized的就夠了;ReentrantLock需要手動(dòng)加鎖和解鎖,且解鎖的操作盡量要放在finally代碼塊中,保證線程正確釋放鎖。ReentrantLock操作較為復(fù)雜,但是因?yàn)榭梢允謩?dòng)控制加鎖和解鎖過(guò)程,在復(fù)雜的并發(fā)場(chǎng)景中能派上用場(chǎng)。
2.ReentrantLock和synchronized都是可重入鎖。
synchronized因?yàn)榭芍厝胍虼丝梢苑旁诒贿f歸執(zhí)行的方法上,且不用擔(dān)心線程最后能否正確釋放鎖;而ReentrantLock在重入時(shí)要卻確保重復(fù)獲取鎖的次數(shù)必須和重復(fù)釋放鎖的次數(shù)一樣,否則可能導(dǎo)致其他線程無(wú)法獲得該鎖。
3.都可以實(shí)現(xiàn)線程之間的等待通知機(jī)制。使用synchronized結(jié)合Object上的wait和notify方法可以實(shí)現(xiàn)線程間的等待通知機(jī)制。ReentrantLock結(jié)合Condition接口同樣可以實(shí)現(xiàn)這個(gè)功能。而且相比前者使用起來(lái)更清晰也更簡(jiǎn)單。
與Synchronized 不同點(diǎn)
- ReentrantLock是Java層面的實(shí)現(xiàn),synchronized是JVM層面的實(shí)現(xiàn)。
- 使用synchronized關(guān)鍵字實(shí)現(xiàn)同步,線程執(zhí)行完同步代碼塊會(huì)自動(dòng)釋放鎖(a 線程執(zhí)行完同步代碼會(huì)釋放鎖 ;b 線程執(zhí)行過(guò)程中發(fā)生異常會(huì)釋放鎖),而ReentrantLock需要手動(dòng)釋放鎖需在finally中手工釋放鎖(unlock()方法釋放鎖),否則容易造成線程死鎖。
- synchronized是非公平鎖,ReentrantLock可以實(shí)現(xiàn)公平和非公平鎖。
- ReentrantLock 可以設(shè)置超時(shí)獲取鎖。在指定的截止時(shí)間之前獲取鎖,如果截止時(shí)間到了還沒(méi)有獲取到鎖,則返回。配合重試機(jī)制更好的解決死鎖。
- ReentrantLock上等待獲取鎖的線程是可中斷的,線程可以放棄等待鎖。而synchonized會(huì)無(wú)限期等待下去。
- ReentrantLock 的 tryLock() 方法可以嘗試非阻塞的獲取鎖,調(diào)用該方法后立刻返回,如果能夠獲取則返回true,否則返回false。
- synchronized無(wú)法判斷是否獲取鎖的狀態(tài),Lock可以判斷是否獲取到鎖,并且可以主動(dòng)嘗試去獲取鎖。
到此這篇關(guān)于一文帶你掌握J(rèn)ava ReentrantLock加解鎖原理的文章就介紹到這了,更多相關(guān)Java ReentrantLock加解鎖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- java ReentrantLock條件鎖實(shí)現(xiàn)原理示例詳解
- Java?synchornized與ReentrantLock處理并發(fā)出現(xiàn)的錯(cuò)誤
- 深入剖析Java ReentrantLock的源碼
- 圖解Java?ReentrantLock的條件變量Condition機(jī)制
- 詳解Java?ReentrantLock可重入,可打斷,鎖超時(shí)的實(shí)現(xiàn)原理
- java?ReentrantLock并發(fā)鎖使用詳解
- 圖解Java?ReentrantLock公平鎖和非公平鎖的實(shí)現(xiàn)
- java鎖機(jī)制ReentrantLock源碼實(shí)例分析
- Java?AQS中ReentrantLock條件鎖的使用
相關(guān)文章
spring boot中interceptor攔截器未生效的解決
這篇文章主要介紹了spring boot中interceptor攔截器未生效的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09Spring Data JPA的Audit功能審計(jì)數(shù)據(jù)庫(kù)的變更
數(shù)據(jù)庫(kù)審計(jì)是指當(dāng)數(shù)據(jù)庫(kù)有記錄變更時(shí),可以記錄數(shù)據(jù)庫(kù)的變更時(shí)間和變更人等,這樣以后出問(wèn)題回溯問(wèn)責(zé)也比較方便,本文討論Spring Data JPA審計(jì)數(shù)據(jù)庫(kù)變更問(wèn)題,感興趣的朋友一起看看吧2021-06-06SpringCloud版本問(wèn)題報(bào)錯(cuò)及解決方法
這篇文章主要介紹了SpringCloud版本問(wèn)題報(bào)錯(cuò)及解決方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-07-07java -jar后臺(tái)啟動(dòng)的四種方式小結(jié)
這篇文章主要介紹了java -jar后臺(tái)啟動(dòng)的四種方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-09-09Java語(yǔ)法基礎(chǔ)之for語(yǔ)句練習(xí)
以下是對(duì)Java語(yǔ)法基礎(chǔ)中的for語(yǔ)句進(jìn)行了詳細(xì)介紹,需要的朋友可以過(guò)來(lái)參考下2013-07-07java ThreadPoolExecutor 并發(fā)調(diào)用實(shí)例詳解
這篇文章主要介紹了java ThreadPoolExecutor 并發(fā)調(diào)用實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-05-05

使用JPA雙向多對(duì)多關(guān)聯(lián)關(guān)系@ManyToMany

spring cloud consul注冊(cè)的服務(wù)報(bào)錯(cuò)critical的解決