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