摘要:關(guān)于,最后有兩點(diǎn)規(guī)律需要注意當(dāng)?shù)牡却?duì)列隊(duì)首結(jié)點(diǎn)是共享結(jié)點(diǎn),說(shuō)明當(dāng)前寫(xiě)鎖被占用,當(dāng)寫(xiě)鎖釋放時(shí),會(huì)以傳播的方式喚醒頭結(jié)點(diǎn)之后緊鄰的各個(gè)共享結(jié)點(diǎn)。當(dāng)?shù)牡却?duì)列隊(duì)首結(jié)點(diǎn)是獨(dú)占結(jié)點(diǎn),說(shuō)明當(dāng)前讀鎖被使用,當(dāng)讀鎖釋放歸零后,會(huì)喚醒隊(duì)首的獨(dú)占結(jié)點(diǎn)。
本文首發(fā)于一世流云的專欄:https://segmentfault.com/blog...一、本章概述
AQS系列的前四個(gè)章節(jié),已經(jīng)分析了AQS的原理,本章將會(huì)從ReentrantReadWriteLock出發(fā),給出其內(nèi)部利用AQS框架的實(shí)現(xiàn)原理。
ReentrantReadWriteLock(以下簡(jiǎn)稱RRW),也就是讀寫(xiě)鎖,是一個(gè)比較特殊的同步器,特殊之處在于其對(duì)同步狀態(tài)State的定義與ReentrantLock、CountDownLatch都很不同。通過(guò)RRW的分析,我們可以更深刻的了解AQS框架的設(shè)計(jì)思想,以及對(duì)“什么是資源?如何定義資源是否可以被訪問(wèn)?”這一命題有更深刻的理解。
關(guān)于ReentrantReadWriteLock的使用和說(shuō)明,讀者可以參考:Java多線程進(jìn)階(四)—— juc-locks鎖框架:ReentrantReadWriteLock二、本章示例
和之前的章節(jié)一樣,本章也通過(guò)示例來(lái)分析RRW的源碼。
假設(shè)現(xiàn)在有4個(gè)線程,ThreadA、ThreadB、ThreadC、ThreadD。
ThreadA、ThreadB、ThreadD為讀線程,ThreadC為寫(xiě)線程:初始時(shí),構(gòu)造RRM對(duì)象:
private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
private final Lock r = rwl.readLock();
private final Lock w = rwl.writeLock();
//ThreadA調(diào)用讀鎖的lock()方法 //ThreadB調(diào)用讀鎖的lock()方法 //ThreadC調(diào)用寫(xiě)鎖的lock()方法 //ThreadD調(diào)用讀鎖的lock()方法三、RRW的公平策略原理 1. RRW對(duì)象的創(chuàng)建
和ReentrantLock類似,ReentrantReadWriteLock的構(gòu)造器可以選擇公平/非公平策略(默認(rèn)為非公平策略),RRW內(nèi)部的FairSync、NonfairSync是AQS的兩個(gè)子類,分別代表了實(shí)現(xiàn)公平策略和非公平策略的同步器:
ReentrantReadWriteLock提供了方法,分別獲取讀鎖/寫(xiě)鎖:
ReentrantReadWriteLock.ReadLock和ReentrantReadWriteLock.WriteLock其實(shí)就是兩個(gè)實(shí)現(xiàn)了Lock接口的內(nèi)部類:
讀鎖其實(shí)是一種共享鎖,實(shí)現(xiàn)了AQS的共享功能API,可以看到讀鎖的內(nèi)部就是調(diào)用了AQS的acquireShared方法,該方法前面幾章我們已經(jīng)見(jiàn)過(guò)太多次了:
關(guān)鍵來(lái)看下ReentrantReadWriteLock是如何實(shí)現(xiàn)tryAcquireShared方法的:
讀鎖獲取成功的條件如下:
寫(xiě)鎖沒(méi)有被其它線程占用(可被當(dāng)前線程占用,這種情況屬于鎖降級(jí))
等待隊(duì)列中的隊(duì)首沒(méi)有其它線程(公平策略)
讀鎖重入次數(shù)沒(méi)有達(dá)到最大值
CAS操作修改同步狀態(tài)值State成功
如果CAS操作失敗,會(huì)調(diào)用fullTryAcquireShared方法,自旋修改State值:
ThreadA調(diào)用完lock方法后,等待隊(duì)列結(jié)構(gòu)如下:
此時(shí):3. ThreadB調(diào)用讀鎖的lock()方法
寫(xiě)鎖數(shù)量:0
讀鎖數(shù)量:1
由于讀鎖是共享鎖,且此時(shí)寫(xiě)鎖未被占用,所以此時(shí)ThreadB也可以拿到讀鎖:
ThreadB調(diào)用完lock方法后,等待隊(duì)列結(jié)構(gòu)如下:
此時(shí):4. ThreadC調(diào)用寫(xiě)鎖的lock()方法
寫(xiě)鎖數(shù)量:0
讀鎖數(shù)量:2
寫(xiě)鎖其實(shí)是一種獨(dú)占鎖,實(shí)現(xiàn)了AQS的獨(dú)占功能API,可以看到寫(xiě)鎖的內(nèi)部就是調(diào)用了AQS的acquire方法,該方法前面幾章我們已經(jīng)見(jiàn)過(guò)太多次了:
關(guān)鍵來(lái)看下ReentrantReadWriteLock是如何實(shí)現(xiàn)tryAcquire方法的,并沒(méi)有什么特別,就是區(qū)分了兩種情況:
當(dāng)前線程已經(jīng)持有寫(xiě)鎖
寫(xiě)鎖未被占用
ThreadC調(diào)用完lock方法后,由于存在使用中的讀鎖,所以會(huì)調(diào)用acquireQueued并被加入等待隊(duì)列,這個(gè)過(guò)程就是獨(dú)占鎖的請(qǐng)求過(guò)程(AQS[二]),等待隊(duì)列結(jié)構(gòu)如下:
此時(shí):5. ThreadD調(diào)用讀鎖的lock()方法
寫(xiě)鎖數(shù)量:0
讀鎖數(shù)量:2
這個(gè)過(guò)程和ThreadA和ThreadB幾乎一樣,讀鎖是共享鎖,可以重復(fù)獲取,但是有一點(diǎn)區(qū)別:
由于等待隊(duì)列中已經(jīng)有其它線程(ThreadC)排在當(dāng)前線程前,所以readerShouldblock方法會(huì)返回true,這是公平策略的含義。
雖然獲取失敗了,但是后續(xù)調(diào)用fullTryAcquireShared方法,自旋修改State值,正常情況下最終修改成功,代表獲取到讀鎖:
最終等待隊(duì)列結(jié)構(gòu)如下:
此時(shí):6. ThreadA釋放讀鎖
寫(xiě)鎖數(shù)量:0
讀鎖數(shù)量:3
內(nèi)部就是調(diào)用了AQS的releaseShared方法,該方法前面幾章我們已經(jīng)見(jiàn)過(guò)太多次了:
關(guān)鍵來(lái)看下ReentrantReadWriteLock是如何實(shí)現(xiàn)tryReleaseShared方法的,沒(méi)什么特別的,就是將讀鎖數(shù)量減1:
注意:
HoldCounter是個(gè)內(nèi)部類,通過(guò)與ThreadLocal結(jié)合使用保存每個(gè)線程的持有讀鎖數(shù)量,其實(shí)是一種優(yōu)化手段。
此時(shí):7. ThreadB釋放讀鎖
寫(xiě)鎖數(shù)量:0
讀鎖數(shù)量:2
和ThreadA的釋放完全一樣,此時(shí):
寫(xiě)鎖數(shù)量:08. ThreadD釋放讀鎖
讀鎖數(shù)量:1
和ThreadA的釋放幾乎一樣,不同的是此時(shí)讀鎖數(shù)量為0,tryReleaseShared方法返回true:
此時(shí):
寫(xiě)鎖數(shù)量:0
讀鎖數(shù)量:0
因此,會(huì)繼續(xù)調(diào)用doReleaseShared方法,doReleaseShared方法之前在講AQS[四]時(shí)已經(jīng)闡述過(guò)了,就是一個(gè)自旋操作:
該操作會(huì)將ThreadC喚醒:
ThreadC從原阻塞處被喚醒后,進(jìn)入下一次自旋操作,然后調(diào)用tryAcquire方法獲取寫(xiě)鎖成功,并從隊(duì)列中移除:
等待隊(duì)列最終狀態(tài):
此時(shí):10. ThreadC釋放寫(xiě)鎖
寫(xiě)鎖數(shù)量:1
讀鎖數(shù)量:0
其實(shí)就是獨(dú)占鎖的釋放,在AQS[二]中,已經(jīng)闡述過(guò)了,不再贅述。
補(bǔ)充一點(diǎn):如果頭結(jié)點(diǎn)后面還有等待的共享結(jié)點(diǎn),會(huì)以傳播的方式依次喚醒,這個(gè)過(guò)程就是共享結(jié)點(diǎn)的喚醒過(guò)程,并無(wú)區(qū)別。四、總結(jié)
本章通過(guò)ReentrantReadWriteLock的公平策略,分析了RRW的源碼,非公平策略分析方法也是一樣的,非公平和公平的最大區(qū)別在于寫(xiě)鎖的獲取上:
在非公平策略中,寫(xiě)鎖的獲取永遠(yuǎn)不需要排隊(duì),這其實(shí)時(shí)性能優(yōu)化的考慮,因?yàn)榇蠖鄶?shù)情況寫(xiě)鎖涉及的操作時(shí)間耗時(shí)要遠(yuǎn)大于讀鎖,頻次遠(yuǎn)低于讀鎖,這樣可以防止寫(xiě)線程一直處于饑餓狀態(tài)。
關(guān)于ReentrantReadWriteLock,最后有兩點(diǎn)規(guī)律需要注意:
當(dāng)RRW的等待隊(duì)列隊(duì)首結(jié)點(diǎn)是共享結(jié)點(diǎn),說(shuō)明當(dāng)前寫(xiě)鎖被占用,當(dāng)寫(xiě)鎖釋放時(shí),會(huì)以傳播的方式喚醒頭結(jié)點(diǎn)之后緊鄰的各個(gè)共享結(jié)點(diǎn)。
當(dāng)RRW的等待隊(duì)列隊(duì)首結(jié)點(diǎn)是獨(dú)占結(jié)點(diǎn),說(shuō)明當(dāng)前讀鎖被使用,當(dāng)讀鎖釋放歸零后,會(huì)喚醒隊(duì)首的獨(dú)占結(jié)點(diǎn)。
ReentrantReadWriteLock的特殊之處其實(shí)就是用一個(gè)int值表示兩種不同的狀態(tài)(低16位表示寫(xiě)鎖的重入次數(shù),高16位表示讀鎖的使用次數(shù)),并通過(guò)兩個(gè)內(nèi)部類同時(shí)實(shí)現(xiàn)了AQS的兩套API,核心部分與共享/獨(dú)占鎖并無(wú)什么區(qū)別。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/76550.html
摘要:整個(gè)包,按照功能可以大致劃分如下鎖框架原子類框架同步器框架集合框架執(zhí)行器框架本系列將按上述順序分析,分析所基于的源碼為。后,根據(jù)一系列常見(jiàn)的多線程設(shè)計(jì)模式,設(shè)計(jì)了并發(fā)包,其中包下提供了一系列基礎(chǔ)的鎖工具,用以對(duì)等進(jìn)行補(bǔ)充增強(qiáng)。 showImg(https://segmentfault.com/img/remote/1460000016012623); 本文首發(fā)于一世流云專欄:https...
摘要:二接口簡(jiǎn)介可以看做是類的方法的替代品,與配合使用。當(dāng)線程執(zhí)行對(duì)象的方法時(shí),當(dāng)前線程會(huì)立即釋放鎖,并進(jìn)入對(duì)象的等待區(qū),等待其它線程喚醒或中斷。 showImg(https://segmentfault.com/img/remote/1460000016012601); 本文首發(fā)于一世流云的專欄:https://segmentfault.com/blog... 本系列文章中所說(shuō)的juc-...
摘要:好了,繼續(xù)向下執(zhí)行,嘗試獲取鎖失敗后,會(huì)調(diào)用首先通過(guò)方法,將包裝成共享結(jié)點(diǎn),插入等待隊(duì)列,插入完成后隊(duì)列結(jié)構(gòu)如下然后會(huì)進(jìn)入自旋操作,先嘗試獲取一次鎖,顯然此時(shí)是獲取失敗的主線程還未調(diào)用,同步狀態(tài)還是。 showImg(https://segmentfault.com/img/remote/1460000016012541); 本文首發(fā)于一世流云的專欄:https://segmentfa...
摘要:關(guān)于接口的介紹,可以參見(jiàn)多線程進(jìn)階二鎖框架接口。最終線程釋放了鎖,并進(jìn)入阻塞狀態(tài)。當(dāng)線程被通知喚醒時(shí),則是將條件隊(duì)列中的結(jié)點(diǎn)轉(zhuǎn)換成等待隊(duì)列中的結(jié)點(diǎn),之后的處理就和獨(dú)占功能完全一樣。 showImg(https://segmentfault.com/img/remote/1460000016012490); 本文首發(fā)于一世流云的專欄:https://segmentfault.com/bl...
摘要:公平策略在多個(gè)線程爭(zhēng)用鎖的情況下,公平策略傾向于將訪問(wèn)權(quán)授予等待時(shí)間最長(zhǎng)的線程。使用方式的典型調(diào)用方式如下二類原理的源碼非常簡(jiǎn)單,它通過(guò)內(nèi)部類實(shí)現(xiàn)了框架,接口的實(shí)現(xiàn)僅僅是對(duì)的的簡(jiǎn)單封裝,參見(jiàn)原理多線程進(jìn)階七鎖框架獨(dú)占功能剖析 showImg(https://segmentfault.com/img/remote/1460000016012582); 本文首發(fā)于一世流云的專欄:https...
閱讀 2168·2021-09-06 15:02
閱讀 1774·2021-08-13 15:02
閱讀 2337·2019-08-29 14:14
閱讀 1495·2019-08-26 13:55
閱讀 581·2019-08-26 13:46
閱讀 3436·2019-08-26 11:41
閱讀 561·2019-08-26 10:27
閱讀 3292·2019-08-23 15:28