<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          ReentrantReadWriteLock讀寫鎖詳解

          共 10792字,需瀏覽 22分鐘

           ·

          2020-09-02 12:51


          一、讀寫鎖簡介


          現(xiàn)實中有這樣一種場景:對共享資源有讀和寫的操作,且寫操作沒有讀操作那么頻繁。在沒有寫操作的時候,多個線程同時讀一個資源沒有任何問題,所以應該允許多個線程同時讀取共享資源;但是如果一個線程想去寫這些共享資源,就不應該允許其他線程對該資源進行讀和寫的操作了。

           

          針對這種場景,JAVA的并發(fā)包提供了讀寫鎖ReentrantReadWriteLock,它表示兩個鎖,一個是讀操作相關的鎖,稱為共享鎖;一個是寫相關的鎖,稱為排他鎖,描述如下:


          線程進入讀鎖的前提條件:


          沒有其他線程的寫鎖,

          沒有寫請求或者有寫請求,但調(diào)用線程和持有鎖的線程是同一個。


          線程進入寫鎖的前提條件:


          沒有其他線程的讀鎖

          沒有其他線程的寫鎖


          而讀寫鎖有以下三個重要的特性:


          (1)公平選擇性:支持非公平(默認)和公平的鎖獲取方式,吞吐量還是非公平優(yōu)于公平。

          (2)重進入:讀鎖和寫鎖都支持線程重進入。

          (3)鎖降級:遵循獲取寫鎖、獲取讀鎖再釋放寫鎖的次序,寫鎖能夠降級成為讀鎖。


          二、源碼解讀


          我們先來看下 ReentrantReadWriteLock 類的整體結構:


          public?class?ReentrantReadWriteLock?implements?ReadWriteLock, java.io.Serializable?{

          ????/** 讀鎖 */
          ????private?final?ReentrantReadWriteLock.ReadLock readerLock;

          ????/** 寫鎖 */
          ????private?final?ReentrantReadWriteLock.WriteLock writerLock;

          ????final?Sync sync;
          ????
          ????/** 使用默認(非公平)的排序屬性創(chuàng)建一個新的 ReentrantReadWriteLock */
          ????public?ReentrantReadWriteLock()?{
          ????????this(false);
          ????}

          ????/** 使用給定的公平策略創(chuàng)建一個新的 ReentrantReadWriteLock */
          ????public?ReentrantReadWriteLock(boolean?fair)?{
          ????????sync = fair ? new?FairSync() : new?NonfairSync();
          ????????readerLock = new?ReadLock(this);
          ????????writerLock = new?WriteLock(this);
          ????}

          ????/** 返回用于寫入操作的鎖 */
          ????public?ReentrantReadWriteLock.WriteLock writeLock()?{ return?writerLock; }
          ????
          ????/** 返回用于讀取操作的鎖 */
          ????public?ReentrantReadWriteLock.ReadLock readLock()??{ return?readerLock; }


          ????abstract?static?class?Sync?extends?AbstractQueuedSynchronizer?{}

          ????static?final?class?NonfairSync?extends?Sync?{}

          ????static?final?class?FairSync?extends?Sync?{}

          ????public?static?class?ReadLock?implements?Lock, java.io.Serializable?{}

          ????public?static?class?WriteLock?implements?Lock, java.io.Serializable?{}
          }


          1、類的繼承關系


          public?class?ReentrantReadWriteLock
          ???????implements?ReadWriteLock, java.io.Serializable?
          {}


          說明:可以看到,ReentrantReadWriteLock實現(xiàn)了ReadWriteLock接口,ReadWriteLock接口定義了獲取讀鎖和寫鎖的規(guī)范,具體需要實現(xiàn)類去實現(xiàn);同時其還實現(xiàn)了Serializable接口,表示可以進行序列化,在源代碼中可以看到ReentrantReadWriteLock實現(xiàn)了自己的序列化邏輯。


          2、類的內(nèi)部類


          ReentrantReadWriteLock有五個內(nèi)部類,五個內(nèi)部類之間也是相互關聯(lián)的。內(nèi)部類的關系如下圖所示。



          說明:如上圖所示,Sync繼承自AQS、NonfairSync繼承自Sync類、FairSync繼承自Sync類(通過構造函數(shù)傳入的布爾值決定要構造哪一種Sync實例);ReadLock實現(xiàn)了Lock接口、WriteLock也實現(xiàn)了Lock接口。


          Sync類:


          (1)類的繼承關系


          abstract?static?class?Sync?extends?AbstractQueuedSynchronizer?{}


          說明:Sync抽象類繼承自AQS抽象類,Sync類提供了對ReentrantReadWriteLock的支持。


          (2)類的內(nèi)部類


          Sync類內(nèi)部存在兩個內(nèi)部類,分別為HoldCounter和ThreadLocalHoldCounter,其中HoldCounter主要與讀鎖配套使用,其中,HoldCounter源碼如下。


          // 計數(shù)器
          static?final?class?HoldCounter?{
          ????// 計數(shù)
          ????int?count = 0;
          ????// Use id, not reference, to avoid garbage retention
          ????// 獲取當前線程的TID屬性的值
          ????final?long?tid = getThreadId(Thread.currentThread());
          }


          說明:HoldCounter主要有兩個屬性,count和tid,其中count表示某個讀線程重入的次數(shù),tid表示該線程的tid字段的值,該字段可以用來唯一標識一個線程。ThreadLocalHoldCounter的源碼如下


          // 本地線程計數(shù)器
          static?final?class?ThreadLocalHoldCounter
          ????extends?ThreadLocal<HoldCounter>
          {
          ????// 重寫初始化方法,在沒有進行set的情況下,獲取的都是該HoldCounter值
          ????public?HoldCounter initialValue()?{
          ????????return?new?HoldCounter();
          ????}
          }


          說明:ThreadLocalHoldCounter重寫了ThreadLocal的initialValue方法,ThreadLocal類可以將線程與對象相關聯(lián)。在沒有進行set的情況下,get到的均是initialValue方法里面生成的那個HolderCounter對象。


          (3)類的屬性


          abstract?static?class?Sync?extends?AbstractQueuedSynchronizer?{
          ????// 版本序列號
          ????private?static?final?long?serialVersionUID = 6317671515068378041L;
          ????// 高16位為讀鎖,低16位為寫鎖
          ????static?final?int?SHARED_SHIFT = 16;
          ????// 讀鎖單位
          ????static?final?int?SHARED_UNIT = (1?<< SHARED_SHIFT);
          ????// 讀鎖最大數(shù)量
          ????static?final?int?MAX_COUNT = (1?<< SHARED_SHIFT) - 1;
          ????// 寫鎖最大數(shù)量
          ????static?final?int?EXCLUSIVE_MASK = (1?<< SHARED_SHIFT) - 1;
          ????// 本地線程計數(shù)器
          ????private?transient?ThreadLocalHoldCounter readHolds;
          ????// 緩存的計數(shù)器
          ????private?transient?HoldCounter cachedHoldCounter;
          ????// 第一個讀線程
          ????private?transient?Thread firstReader = null;
          ????// 第一個讀線程的計數(shù)
          ????private?transient?int?firstReaderHoldCount;
          }


          說明:該屬性中包括了讀鎖、寫鎖線程的最大量。本地線程計數(shù)器等。


          (4)類的構造函數(shù)


          // 構造函數(shù)
          Sync() {
          ????// 本地線程計數(shù)器
          ????readHolds = new?ThreadLocalHoldCounter();
          ????// 設置AQS的狀態(tài)
          ????setState(getState()); // ensures visibility of readHolds
          }


          說明:在Sync的構造函數(shù)中設置了本地線程計數(shù)器和AQS的狀態(tài)state。


          3、讀寫狀態(tài)的設計


          同步狀態(tài)在重入鎖的實現(xiàn)中是表示被同一個線程重復獲取的次數(shù),即一個整形變量來維護,但是之前的那個表示僅僅表示是否鎖定,而不用區(qū)分是讀鎖還是寫鎖。而讀寫鎖需要在同步狀態(tài)(一個整形變量)上維護多個讀線程和一個寫線程的狀態(tài)。


          讀寫鎖對于同步狀態(tài)的實現(xiàn)是在一個整形變量上通過“按位切割使用”:將變量切割成兩部分,高16位表示讀,低16位表示寫。



          假設當前同步狀態(tài)值為S,get和set的操作如下:


          (1)獲取寫狀態(tài):

          ??? S&0x0000FFFF:將高16位全部抹去

          (2)獲取讀狀態(tài):

          ??? S>>>16:無符號補0,右移16位

          (3)寫狀態(tài)加1:

          ???? S+1

          (4)讀狀態(tài)加1:

            S+(1<<16)即S + 0x00010000


          在代碼層的判斷中,如果S不等于0,當寫狀態(tài)(S&0x0000FFFF),而讀狀態(tài)(S>>>16)大于0,則表示該讀寫鎖的讀鎖已被獲取。


          4、寫鎖的獲取與釋放


          看下WriteLock類中的lock和unlock方法:


          public?void?lock()?{
          ????sync.acquire(1);
          }

          public?void?unlock()?{
          ????sync.release(1);
          }


          可以看到就是調(diào)用的獨占式同步狀態(tài)的獲取與釋放,因此真實的實現(xiàn)就是Sync的 tryAcquire和 tryRelease。


          寫鎖的獲取,看下tryAcquire:


          protected?final?boolean?tryAcquire(int?acquires)?{
          ????//當前線程
          ????Thread current = Thread.currentThread();
          ????//獲取狀態(tài)
          ????int?c = getState();
          ????//寫線程數(shù)量(即獲取獨占鎖的重入數(shù))
          ????int?w = exclusiveCount(c);

          ????//當前同步狀態(tài)state != 0,說明已經(jīng)有其他線程獲取了讀鎖或寫鎖
          ????if?(c != 0) {
          ????????// 當前state不為0,此時:如果寫鎖狀態(tài)為0說明讀鎖此時被占用返回false;
          ????????// 如果寫鎖狀態(tài)不為0且寫鎖沒有被當前線程持有返回false
          ????????if?(w == 0?|| current != getExclusiveOwnerThread())
          ????????????return?false;

          ????????//判斷同一線程獲取寫鎖是否超過最大次數(shù)(65535),支持可重入
          ????????if?(w + exclusiveCount(acquires) > MAX_COUNT)
          ????????????throw?new?Error("Maximum lock count exceeded");
          ????????//更新狀態(tài)
          ????????//此時當前線程已持有寫鎖,現(xiàn)在是重入,所以只需要修改鎖的數(shù)量即可。
          ????????setState(c + acquires);
          ????????return?true;
          ????}

          ????//到這里說明此時c=0,讀鎖和寫鎖都沒有被獲取
          ????//writerShouldBlock表示是否阻塞
          ????if?(writerShouldBlock() ||
          ????????!compareAndSetState(c, c + acquires))
          ????????return?false;

          ????//設置鎖為當前線程所有
          ????setExclusiveOwnerThread(current);
          ????return?true;
          }


          其中exclusiveCount方法表示占有寫鎖的線程數(shù)量,源碼如下:


          static?int?exclusiveCount(int?c)?{ return?c & EXCLUSIVE_MASK; }


          說明:直接將狀態(tài)state和(2^16 - 1)做與運算,其等效于將state模上2^16。寫鎖數(shù)量由state的低十六位表示。


          從源代碼可以看出,獲取寫鎖的步驟如下:


          (1)首先獲取c、w。c表示當前鎖狀態(tài);w表示寫線程數(shù)量。然后判斷同步狀態(tài)state是否為0。如果state!=0,說明已經(jīng)有其他線程獲取了讀鎖或寫鎖,執(zhí)行(2);否則執(zhí)行(5)。


          (2)如果鎖狀態(tài)不為零(c != 0),而寫鎖的狀態(tài)為0(w = 0),說明讀鎖此時被其他線程占用,所以當前線程不能獲取寫鎖,自然返回false。或者鎖狀態(tài)不為零,而寫鎖的狀態(tài)也不為0,但是獲取寫鎖的線程不是當前線程,則當前線程也不能獲取寫鎖。


          (3)判斷當前線程獲取寫鎖是否超過最大次數(shù),若超過,拋異常,反之更新同步狀態(tài)(此時當前線程已獲取寫鎖,更新是線程安全的),返回true。


          (4)如果state為0,此時讀鎖或寫鎖都沒有被獲取,判斷是否需要阻塞(公平和非公平方式實現(xiàn)不同),在非公平策略下總是不會被阻塞,在公平策略下會進行判斷(判斷同步隊列中是否有等待時間更長的線程,若存在,則需要被阻塞,否則,無需阻塞),如果不需要阻塞,則CAS更新同步狀態(tài),若CAS成功則返回true,失敗則說明鎖被別的線程搶去了,返回false。如果需要阻塞則也返回false。


          5)成功獲取寫鎖后,將當前線程設置為占有寫鎖的線程,返回true。


          方法流程圖如下:



          寫鎖的釋放,tryRelease方法:


          protected?final?boolean?tryRelease(int?releases)?{
          ????//若鎖的持有者不是當前線程,拋出異常
          ????if?(!isHeldExclusively())
          ????????throw?new?IllegalMonitorStateException();
          ????//寫鎖的新線程數(shù)
          ????int?nextc = getState() - releases;
          ????//如果獨占模式重入數(shù)為0了,說明獨占模式被釋放
          ????boolean?free = exclusiveCount(nextc) == 0;
          ????if?(free)
          ????????//若寫鎖的新線程數(shù)為0,則將鎖的持有者設置為null
          ????????setExclusiveOwnerThread(null);
          ????//設置寫鎖的新線程數(shù)
          ????//不管獨占模式是否被釋放,更新獨占重入數(shù)
          ????setState(nextc);
          ????return?free;
          }


          寫鎖的釋放過程還是相對而言比較簡單的:首先查看當前線程是否為寫鎖的持有者,如果不是拋出異常。然后檢查釋放后寫鎖的線程數(shù)是否為0,如果為0則表示寫鎖空閑了,釋放鎖資源將鎖的持有線程設置為null,否則釋放僅僅只是一次重入鎖而已,并不能將寫鎖的線程清空。


          說明:此方法用于釋放寫鎖資源,首先會判斷該線程是否為獨占線程,若不為獨占線程,則拋出異常,否則,計算釋放資源后的寫鎖的數(shù)量,若為0,表示成功釋放,資源不將被占用,否則,表示資源還被占用。其方法流程圖如下。



          5、讀鎖的獲取與釋放


          類似于寫鎖,讀鎖的lock和unlock的實際實現(xiàn)對應Sync的 tryAcquireShared 和 tryReleaseShared方法。


          讀鎖的獲取,看下tryAcquireShared方法


          protected?final int?tryAcquireShared(int?unused) {
          ????// 獲取當前線程
          ????Thread current = Thread.currentThread();
          ????// 獲取狀態(tài)
          ????int?c = getState();

          ????//如果寫鎖線程數(shù) != 0 ,且獨占鎖不是當前線程則返回失敗,因為存在鎖降級
          ????if?(exclusiveCount(c) != 0?&&
          ????????getExclusiveOwnerThread() != current)
          ????????return?-1;
          ????// 讀鎖數(shù)量
          ????int?r = sharedCount(c);
          ????/*
          ?????* readerShouldBlock():讀鎖是否需要等待(公平鎖原則)
          ?????* r < MAX_COUNT:持有線程小于最大數(shù)(65535)
          ?????* compareAndSetState(c, c + SHARED_UNIT):設置讀取鎖狀態(tài)
          ?????*/

          ?????// 讀線程是否應該被阻塞、并且小于最大值、并且比較設置成功
          ????if?(!readerShouldBlock() &&
          ????????r < MAX_COUNT &&
          ????????compareAndSetState(c, c + SHARED_UNIT)) {
          ????????//r == 0,表示第一個讀鎖線程,第一個讀鎖firstRead是不會加入到readHolds中
          ????????if?(r == 0) { // 讀鎖數(shù)量為0
          ????????????// 設置第一個讀線程
          ????????????firstReader = current;
          ????????????// 讀線程占用的資源數(shù)為1
          ????????????firstReaderHoldCount = 1;
          ????????} else?if?(firstReader == current) { // 當前線程為第一個讀線程,表示第一個讀鎖線程重入
          ????????????// 占用資源數(shù)加1
          ????????????firstReaderHoldCount++;
          ????????} else?{ // 讀鎖數(shù)量不為0并且不為當前線程
          ????????????// 獲取計數(shù)器
          ????????????HoldCounter rh = cachedHoldCounter;
          ????????????// 計數(shù)器為空或者計數(shù)器的tid不為當前正在運行的線程的tid
          ????????????if?(rh == null?|| rh.tid != getThreadId(current))
          ????????????????// 獲取當前線程對應的計數(shù)器
          ????????????????cachedHoldCounter = rh = readHolds.get();
          ????????????else?if?(rh.count == 0) // 計數(shù)為0
          ????????????????//加入到readHolds中
          ????????????????readHolds.set(rh);
          ????????????//計數(shù)+1
          ????????????rh.count++;
          ????????}
          ????????return?1;
          ????}
          ????return?fullTryAcquireShared(current);
          }


          ?其中sharedCount方法表示占有讀鎖的線程數(shù)量,源碼如下:


          static?int?sharedCount(int?c)????{ return?c >>> SHARED_SHIFT; }


          說明:直接將state右移16位,就可以得到讀鎖的線程數(shù)量,因為state的高16位表示讀鎖,對應的第十六位表示寫鎖數(shù)量。


          讀鎖獲取鎖的過程比寫鎖稍微復雜些,首先判斷寫鎖是否為0并且當前線程不占有獨占鎖,直接返回;否則,判斷讀線程是否需要被阻塞并且讀鎖數(shù)量是否小于最大值并且比較設置狀態(tài)成功,若當前沒有讀鎖,則設置第一個讀線程firstReader和firstReaderHoldCount;若當前線程線程為第一個讀線程,則增加firstReaderHoldCount;否則,將設置當前線程對應的HoldCounter對象的值。流程圖如下。



          注意:更新成功后會在firstReaderHoldCount中或readHolds(ThreadLocal類型的)的本線程副本中記錄當前線程重入數(shù)(23行至43行代碼),這是為了實現(xiàn)jdk1.6中加入的getReadHoldCount()方法的,這個方法能獲取當前線程重入共享鎖的次數(shù)(state中記錄的是多個線程的總重入次數(shù)),加入了這個方法讓代碼復雜了不少,但是其原理還是很簡單的:如果當前只有一個線程的話,還不需要動用ThreadLocal,直接往firstReaderHoldCount這個成員變量里存重入數(shù),當有第二個線程來的時候,就要動用ThreadLocal變量readHolds了,每個線程擁有自己的副本,用來保存自己的重入數(shù)。


          fullTryAcquireShared方法:


          final int?fullTryAcquireShared(Thread current) {

          ????HoldCounter rh = null;
          ????for?(;;) { // 無限循環(huán)
          ????????// 獲取狀態(tài)
          ????????int?c = getState();
          ????????if?(exclusiveCount(c) != 0) { // 寫線程數(shù)量不為0
          ????????????if?(getExclusiveOwnerThread() != current) // 不為當前線程
          ????????????????return?-1;
          ????????} else?if?(readerShouldBlock()) { // 寫線程數(shù)量為0并且讀線程被阻塞
          ????????????// Make sure we're not acquiring read lock reentrantly
          ????????????if?(firstReader == current) { // 當前線程為第一個讀線程
          ????????????????// assert firstReaderHoldCount > 0;
          ????????????} else?{ // 當前線程不為第一個讀線程
          ????????????????if?(rh == null) { // 計數(shù)器不為空
          ????????????????????//
          ????????????????????rh = cachedHoldCounter;
          ????????????????????if?(rh == null?|| rh.tid != getThreadId(current)) { // 計數(shù)器為空或者計數(shù)器的tid不為當前正在運行的線程的tid
          ????????????????????????rh = readHolds.get();
          ????????????????????????if?(rh.count == 0)
          ????????????????????????????readHolds.remove();
          ????????????????????}
          ????????????????}
          ????????????????if?(rh.count == 0)
          ????????????????????return?-1;
          ????????????}
          ????????}
          ????????if?(sharedCount(c) == MAX_COUNT) // 讀鎖數(shù)量為最大值,拋出異常
          ????????????throw?new?Error("Maximum lock count exceeded");
          ????????if?(compareAndSetState(c, c + SHARED_UNIT)) { // 比較并且設置成功
          ????????????if?(sharedCount(c) == 0) { // 讀線程數(shù)量為0
          ????????????????// 設置第一個讀線程
          ????????????????firstReader = current;
          ????????????????//
          ????????????????firstReaderHoldCount = 1;
          ????????????} else?if?(firstReader == current) {
          ????????????????firstReaderHoldCount++;
          ????????????} else?{
          ????????????????if?(rh == null)
          ????????????????????rh = cachedHoldCounter;
          ????????????????if?(rh == null?|| rh.tid != getThreadId(current))
          ????????????????????rh = readHolds.get();
          ????????????????else?if?(rh.count == 0)
          ????????????????????readHolds.set(rh);
          ????????????????rh.count++;
          ????????????????cachedHoldCounter = rh; // cache for release
          ????????????}
          ????????????return?1;
          ????????}
          ????}
          }


          說明:在tryAcquireShared函數(shù)中,如果下列三個條件不滿足(讀線程是否應該被阻塞、小于最大值、比較設置成功)則會進行fullTryAcquireShared函數(shù)中,它用來保證相關操作可以成功。其邏輯與tryAcquireShared邏輯類似,不再累贅。


          讀鎖的釋放,tryReleaseShared方法


          protected?final boolean tryReleaseShared(int?unused) {
          ????// 獲取當前線程
          ????Thread current = Thread.currentThread();
          ????if?(firstReader == current) { // 當前線程為第一個讀線程
          ????????// assert firstReaderHoldCount > 0;
          ????????if?(firstReaderHoldCount == 1) // 讀線程占用的資源數(shù)為1
          ????????????firstReader = null;
          ????????else?// 減少占用的資源
          ????????????firstReaderHoldCount--;
          ????} else?{ // 當前線程不為第一個讀線程
          ????????// 獲取緩存的計數(shù)器
          ????????HoldCounter rh = cachedHoldCounter;
          ????????if?(rh == null?|| rh.tid != getThreadId(current)) // 計數(shù)器為空或者計數(shù)器的tid不為當前正在運行的線程的tid
          ????????????// 獲取當前線程對應的計數(shù)器
          ????????????rh = readHolds.get();
          ????????// 獲取計數(shù)
          ????????int?count = rh.count;
          ????????if?(count <= 1) { // 計數(shù)小于等于1
          ????????????// 移除
          ????????????readHolds.remove();
          ????????????if?(count <= 0) // 計數(shù)小于等于0,拋出異常
          ????????????????throw?unmatchedUnlockException();
          ????????}
          ????????// 減少計數(shù)
          ????????--rh.count;
          ????}
          ????for?(;;) { // 無限循環(huán)
          ????????// 獲取狀態(tài)
          ????????int?c = getState();
          ????????// 獲取狀態(tài)
          ????????int?nextc = c - SHARED_UNIT;
          ????????if?(compareAndSetState(c, nextc)) // 比較并進行設置
          ????????????// Releasing the read lock has no effect on readers,
          ????????????// but it may allow waiting writers to proceed if
          ????????????// both read and write locks are now free.
          ????????????return?nextc == 0;
          ????}
          }


          說明:此方法表示讀鎖線程釋放鎖。首先判斷當前線程是否為第一個讀線程firstReader,若是,則判斷第一個讀線程占有的資源數(shù)firstReaderHoldCount是否為1,若是,則設置第一個讀線程firstReader為空,否則,將第一個讀線程占有的資源數(shù)firstReaderHoldCount減1;若當前線程不是第一個讀線程,那么首先會獲取緩存計數(shù)器(上一個讀鎖線程對應的計數(shù)器 ),若計數(shù)器為空或者tid不等于當前線程的tid值,則獲取當前線程的計數(shù)器,如果計數(shù)器的計數(shù)count小于等于1,則移除當前線程對應的計數(shù)器,如果計數(shù)器的計數(shù)count小于等于0,則拋出異常,之后再減少計數(shù)即可。無論何種情況,都會進入無限循環(huán),該循環(huán)可以確保成功設置狀態(tài)state。其流程圖如下。


          ? ??

          在讀鎖的獲取、釋放過程中,總是會有一個對象存在著,同時該對象在獲取線程獲取讀鎖是+1,釋放讀鎖時-1,該對象就是HoldCounter。

          ? ??

          要明白HoldCounter就要先明白讀鎖。前面提過讀鎖的內(nèi)在實現(xiàn)機制就是共享鎖,對于共享鎖其實我們可以稍微的認為它不是一個鎖的概念,它更加像一個計數(shù)器的概念。一次共享鎖操作就相當于一次計數(shù)器的操作,獲取共享鎖計數(shù)器+1,釋放共享鎖計數(shù)器-1。只有當線程獲取共享鎖后才能對共享鎖進行釋放、重入操作。所以HoldCounter的作用就是當前線程持有共享鎖的數(shù)量,這個數(shù)量必須要與線程綁定在一起,否則操作其他線程鎖就會拋出異常。


          先看讀鎖獲取鎖的部分:


          if?(r == 0) {//r == 0,表示第一個讀鎖線程,第一個讀鎖firstRead是不會加入到readHolds中
          ????firstReader = current;
          ????firstReaderHoldCount = 1;
          } else?if?(firstReader == current) {//第一個讀鎖線程重入
          ????firstReaderHoldCount++;
          } else?{ //非firstReader計數(shù)
          ????HoldCounter rh = cachedHoldCounter;//readHoldCounter緩存
          ????//rh == null 或者 rh.tid != current.getId(),需要獲取rh
          ????if?(rh == null?|| rh.tid != current.getId())
          ????????cachedHoldCounter = rh = readHolds.get();
          ????else?if?(rh.count == 0)
          ????????readHolds.set(rh); //加入到readHolds中
          ????rh.count++; //計數(shù)+1
          }


          這里為什么要搞一個firstRead、firstReaderHoldCount呢?而不是直接使用else那段代碼?這是為了一個效率問題,firstReader是不會放入到readHolds中的,如果讀鎖僅有一個的情況下就會避免查找readHolds。可能就看這個代碼還不是很理解HoldCounter。我們先看firstReader、firstReaderHoldCount的定義:


          private?transient?Thread firstReader = null;
          private?transient?int?firstReaderHoldCount;


          這兩個變量比較簡單,一個表示線程,當然該線程是一個特殊的線程,一個是firstReader的重入計數(shù)。


          HoldCounter的定義:


          static?final?class?HoldCounter?{
          ????int?count = 0;
          ????final?long?tid = Thread.currentThread().getId();
          }


          在HoldCounter中僅有count和tid兩個變量,其中count代表著計數(shù)器,tid是線程的id。但是如果要將一個對象和線程綁定起來僅記錄tid肯定不夠的,而且HoldCounter根本不能起到綁定對象的作用,只是記錄線程tid而已。

          ? ??

          誠然,在java中,我們知道如果要將一個線程和對象綁定在一起只有ThreadLocal才能實現(xiàn)。所以如下:


          static?final?class?ThreadLocalHoldCounter
          ????extends?ThreadLocal<HoldCounter>
          {
          ????public?HoldCounter initialValue()?{
          ????????return?new?HoldCounter();
          ????}
          }


          ThreadLocalHoldCounter繼承ThreadLocal,并且重寫了initialValue方法。

          ? ?

          故而,HoldCounter應該就是綁定線程上的一個計數(shù)器,而ThradLocalHoldCounter則是線程綁定的ThreadLocal。從上面我們可以看到ThreadLocal將HoldCounter綁定到當前線程上,同時HoldCounter也持有線程Id,這樣在釋放鎖的時候才能知道ReadWriteLock里面緩存的上一個讀取線程(cachedHoldCounter)是否是當前線程。這樣做的好處是可以減少ThreadLocal.get()的次數(shù),因為這也是一個耗時操作。需要說明的是這樣HoldCounter綁定線程id而不綁定線程對象的原因是避免HoldCounter和ThreadLocal互相綁定而GC難以釋放它們(盡管GC能夠智能的發(fā)現(xiàn)這種引用而回收它們,但是這需要一定的代價),所以其實這樣做只是為了幫助GC快速回收對象而已。


          三、總結

          ? ?

          通過上面的源碼分析,我們可以發(fā)現(xiàn)一個現(xiàn)象:

          ? ?

          在線程持有讀鎖的情況下,該線程不能取得寫鎖(因為獲取寫鎖的時候,如果發(fā)現(xiàn)當前的讀鎖被占用,就馬上獲取失敗,不管讀鎖是不是被當前線程持有)。

          ? ?

          在線程持有寫鎖的情況下,該線程可以繼續(xù)獲取讀鎖(獲取讀鎖時如果發(fā)現(xiàn)寫鎖被占用,只有寫鎖沒有被當前線程占用的情況才會獲取失敗)。

          ??

          仔細想想,這個設計是合理的:因為當線程獲取讀鎖的時候,可能有其他線程同時也在持有讀鎖,因此不能把獲取讀鎖的線程“升級”為寫鎖;而對于獲得寫鎖的線程,它一定獨占了讀寫鎖,因此可以繼續(xù)讓它獲取讀鎖,當它同時獲取了寫鎖和讀鎖后,還可以先釋放寫鎖繼續(xù)持有讀鎖,這樣一個寫鎖就“降級”為了讀鎖。


          綜上:


          一個線程要想同時持有寫鎖和讀鎖,必須先獲取寫鎖再獲取讀鎖;寫鎖可以“降級”為讀鎖;讀鎖不能“升級”為寫鎖。



          原文鏈接:cnblogs.com/xiaoxi/p/9140541.html



          瀏覽 33
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  视频免费一区 | 五月天丁香婷婷亚洲无码 | 亚洲色鬼 | 秋霞福利网| 超碰自拍99 |