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