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