摘要:好了,繼續(xù)向下執(zhí)行,嘗試獲取鎖失敗后,會(huì)調(diào)用首先通過方法,將包裝成共享結(jié)點(diǎn),插入等待隊(duì)列,插入完成后隊(duì)列結(jié)構(gòu)如下然后會(huì)進(jìn)入自旋操作,先嘗試獲取一次鎖,顯然此時(shí)是獲取失敗的主線程還未調(diào)用,同步狀態(tài)還是。
本文首發(fā)于一世流云的專欄:https://segmentfault.com/blog...一、本章概述
AQS系列的前三個(gè)章節(jié),我們通過ReentrantLock的示例,分析了AQS的獨(dú)占功能。
本章將以CountDownLatch為例,分析AQS的共享功能。CountDownLatch,是J.U.C中的一個(gè)同步器類,可作為倒數(shù)計(jì)數(shù)器使用,關(guān)于CountDownLatch的使用和說明,讀者可以參考:
Java多線程進(jìn)階(十八)—— J.U.C之synchronizer框架:CountDownLatch。
CountDownLatch示例
假設(shè)現(xiàn)在有3個(gè)線程,ThreadA、ThreadB、mainThread,CountDownLatch初始計(jì)數(shù)為1:
CountDownLatch switcher = new CountDownLatch(1);
線程的調(diào)用時(shí)序如下:
//ThreadA調(diào)用await()方法等待 //ThreadB調(diào)用await()方法等待 //主線程main調(diào)用countDown()放行二、AQS共享功能的原理 1. 創(chuàng)建CountDownLatch
CountDownLatch的創(chuàng)建沒什么特殊,調(diào)用唯一的構(gòu)造器,傳入一個(gè)初始計(jì)數(shù)值,內(nèi)部實(shí)例化一個(gè)AQS子類:
CountDownLatch switcher = new CountDownLatch(1);
可以看到,初始計(jì)數(shù)值count其實(shí)就是同步狀態(tài)值,在CountDownLatch中,同步狀態(tài)State表示CountDownLatch的計(jì)數(shù)器的初始大小。
2. ThreadA調(diào)用await()方法等待CountDownLatch的await方法是響應(yīng)中斷的,該方法其實(shí)是調(diào)用了AQS的acquireSharedInterruptibly方法:
注意tryAcquireShared方法,該方法嘗試獲取鎖,由AQS子類實(shí)現(xiàn),其返回值的含義如下:
State | 資源的定義 |
---|---|
小于0 | 表示獲取失敗 |
0 | 表示獲取成功 |
大于0 | 表示獲取成功,且后繼爭(zhēng)用線程可能成功 |
CountDownLatch中的tryAcquireShared實(shí)現(xiàn)相當(dāng)簡(jiǎn)單,當(dāng)State值為0時(shí),永遠(yuǎn)返回成功:
我們之前說了在CountDownLatch中,同步狀態(tài)State表示CountDownLatch的計(jì)數(shù)器的初始值,當(dāng)State==0時(shí),表示無鎖狀態(tài),且一旦State變?yōu)?,就永遠(yuǎn)處于無鎖狀態(tài)了,此時(shí)所有線程在await上等待的線程都可以繼續(xù)執(zhí)行。
而在ReentrantLock中,State==0時(shí),雖然也表示無鎖狀態(tài),但是只有一個(gè)線程可以重置State的值。這就是共享鎖的含義。
好了,繼續(xù)向下執(zhí)行,ThreadA嘗試獲取鎖失敗后,會(huì)調(diào)用doAcquireSharedInterruptibly:
首先通過addWaiter方法,將ThreadA包裝成共享結(jié)點(diǎn),插入等待隊(duì)列,插入完成后隊(duì)列結(jié)構(gòu)如下:
然后會(huì)進(jìn)入自旋操作,先嘗試獲取一次鎖,顯然此時(shí)是獲取失敗的(主線程main還未調(diào)用countDown,同步狀態(tài)State還是1)。
然后判斷是否要進(jìn)入阻塞(shouldParkAfterFailedAcquire):
好了,至此,ThreadA進(jìn)入阻塞態(tài),最終隊(duì)列結(jié)構(gòu)如下:
流程和步驟2完全相同,調(diào)用后ThreadB也被加入到等待隊(duì)列中:
ThreadA和ThreadB調(diào)用了await()方法后都在等待了,現(xiàn)在主線程main開始調(diào)用countDown()方法,該方法調(diào)用后,ThreadA和ThreadB都會(huì)被喚醒,并繼續(xù)往下執(zhí)行,達(dá)到類似門栓的作用。
來看下countDown方法的內(nèi)部:
該方法內(nèi)部調(diào)用了AQS的releaseShared方法,先嘗試一次釋放鎖,tryReleaseShared方法是一個(gè)鉤子方法,由CountDownLatch實(shí)現(xiàn),當(dāng)同步State狀態(tài)值首次變?yōu)?時(shí),會(huì)返回true:
先調(diào)用compareAndSetWaitStatus將頭結(jié)點(diǎn)的等待狀態(tài)置為0,表示將喚醒后續(xù)結(jié)點(diǎn)(ThreadA),成功后的等待隊(duì)列結(jié)構(gòu)如下:
然后調(diào)用unparkSuccessor喚醒后繼結(jié)點(diǎn)(ThreadA被喚醒后會(huì)從原阻塞處繼續(xù)往下執(zhí)行,這個(gè)在步驟5再講):
此時(shí),等待隊(duì)列結(jié)構(gòu)如下:
ThreadA被喚醒后,會(huì)從原來的阻塞處繼續(xù)向下執(zhí)行:
由于是一個(gè)自旋操作,ThreadA會(huì)再次嘗試獲取鎖,由于此時(shí)State同步狀態(tài)值為0(無鎖狀態(tài)),所以獲取成功。然后調(diào)用setHeadAndPropagate方法:
setHeadAndPropagate方法把ThreadA結(jié)點(diǎn)變?yōu)轭^結(jié)點(diǎn),并根據(jù)傳播狀態(tài)判斷是否要喚醒并釋放后繼結(jié)點(diǎn):
①將ThreadA變成頭結(jié)點(diǎn)
②調(diào)用doReleaseShared方法,釋放并喚醒ThreadB結(jié)點(diǎn)
ThreadB被喚醒后,從原阻塞處繼續(xù)向下執(zhí)行,這個(gè)過程和步驟5(ThreadA喚醒后繼續(xù)執(zhí)行)完全一樣。
setHeadAndPropagate方法把ThreadB結(jié)點(diǎn)變?yōu)轭^結(jié)點(diǎn),并根據(jù)傳播狀態(tài)判斷是否要喚醒并釋放后繼結(jié)點(diǎn):
①將ThreadB變成頭結(jié)點(diǎn)
②調(diào)用doReleaseShared方法,釋放并喚醒后繼結(jié)點(diǎn)(此時(shí)沒有后繼結(jié)點(diǎn)了,則直接break):
最終隊(duì)列狀態(tài)如下:
AQS的共享功能,通過鉤子方法tryAcquireShared暴露,與獨(dú)占功能最主要的區(qū)別就是:
共享功能的結(jié)點(diǎn),一旦被喚醒,會(huì)向隊(duì)列后部傳播(Propagate)狀態(tài),以實(shí)現(xiàn)共享結(jié)點(diǎn)的連續(xù)喚醒。這也是共享的含義,當(dāng)鎖被釋放時(shí),所有持有該鎖的共享線程都會(huì)被喚醒,并從等待隊(duì)列移除。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/76551.html
摘要:開始獲取鎖終于輪到出場(chǎng)了,的調(diào)用過程和完全一樣,同樣拿不到鎖,然后加入到等待隊(duì)列隊(duì)尾然后,在阻塞前需要把前驅(qū)結(jié)點(diǎn)的狀態(tài)置為,以確保將來可以被喚醒至此,的執(zhí)行也暫告一段落了安心得在等待隊(duì)列中睡覺。 showImg(https://segmentfault.com/img/remote/1460000016012467); 本文首發(fā)于一世流云的專欄:https://segmentfault...
摘要:線程可以調(diào)用的方法進(jìn)入阻塞,當(dāng)計(jì)數(shù)值降到時(shí),所有之前調(diào)用阻塞的線程都會(huì)釋放。注意的初始計(jì)數(shù)值一旦降到,無法重置。 showImg(https://segmentfault.com/img/remote/1460000016012041); 本文首發(fā)于一世流云的專欄:https://segmentfault.com/blog... 一、CountDownLatch簡(jiǎn)介 CountDow...
摘要:公平策略在多個(gè)線程爭(zhēng)用鎖的情況下,公平策略傾向于將訪問權(quán)授予等待時(shí)間最長(zhǎng)的線程。使用方式的典型調(diào)用方式如下二類原理的源碼非常簡(jiǎn)單,它通過內(nèi)部類實(shí)現(xiàn)了框架,接口的實(shí)現(xiàn)僅僅是對(duì)的的簡(jiǎn)單封裝,參見原理多線程進(jìn)階七鎖框架獨(dú)占功能剖析 showImg(https://segmentfault.com/img/remote/1460000016012582); 本文首發(fā)于一世流云的專欄:https...
摘要:關(guān)于,最后有兩點(diǎn)規(guī)律需要注意當(dāng)?shù)牡却?duì)列隊(duì)首結(jié)點(diǎn)是共享結(jié)點(diǎn),說明當(dāng)前寫鎖被占用,當(dāng)寫鎖釋放時(shí),會(huì)以傳播的方式喚醒頭結(jié)點(diǎn)之后緊鄰的各個(gè)共享結(jié)點(diǎn)。當(dāng)?shù)牡却?duì)列隊(duì)首結(jié)點(diǎn)是獨(dú)占結(jié)點(diǎn),說明當(dāng)前讀鎖被使用,當(dāng)讀鎖釋放歸零后,會(huì)喚醒隊(duì)首的獨(dú)占結(jié)點(diǎn)。 showImg(https://segmentfault.com/img/remote/1460000016012293); 本文首發(fā)于一世流云的專欄:...
摘要:整個(gè)包,按照功能可以大致劃分如下鎖框架原子類框架同步器框架集合框架執(zhí)行器框架本系列將按上述順序分析,分析所基于的源碼為。后,根據(jù)一系列常見的多線程設(shè)計(jì)模式,設(shè)計(jì)了并發(fā)包,其中包下提供了一系列基礎(chǔ)的鎖工具,用以對(duì)等進(jìn)行補(bǔ)充增強(qiáng)。 showImg(https://segmentfault.com/img/remote/1460000016012623); 本文首發(fā)于一世流云專欄:https...
閱讀 1634·2023-04-26 02:11
閱讀 2979·2023-04-25 16:18
閱讀 3711·2021-09-06 15:00
閱讀 2630·2019-08-30 15:55
閱讀 1934·2019-08-30 13:20
閱讀 2051·2019-08-26 18:36
閱讀 3121·2019-08-26 11:40
閱讀 2537·2019-08-26 10:11