乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      圖解ReentrantReadWriteLock實(shí)現(xiàn)分析

       沙門空海 2020-03-24

      概述

      本文主要分析JCU包中讀寫鎖接口(ReadWriteLock)的重要實(shí)現(xiàn)類ReentrantReadWriteLock。主要實(shí)現(xiàn)讀共享,寫互斥功能,對(duì)比單純的互斥鎖在共享資源使用場(chǎng)景為頻繁讀取及少量修改的情況下可以較好的提高性能。

      ReadWriteLock接口簡(jiǎn)單說(shuō)明

      ReadWriteLock接口只定義了兩個(gè)方法:

      public interface ReadWriteLock { /** * Returns the lock used for reading. * * @return the lock used for reading */ Lock readLock(); /** * Returns the lock used for writing. * * @return the lock used for writing */ Lock writeLock();}

      通過(guò)調(diào)用相應(yīng)方法獲取讀鎖或?qū)戞i,獲取的讀鎖及寫鎖都是Lock接口的實(shí)現(xiàn),可以如同使用Lock接口一樣使用(其實(shí)也有一些特性是不支持的)。

      ReentrantReadWriteLock使用示例

      讀寫鎖的使用并不復(fù)雜,可以參考以下使用示例:

         class RWDictionary {    private final Map<String, Data> m = new TreeMap<String, Data>();    private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();    private final Lock r = rwl.readLock();    private final Lock w = rwl.writeLock();     public Data get(String key) {      r.lock();      try { return m.get(key); }      finally { r.unlock(); }    }    public String[] allKeys() {      r.lock();      try { return m.keySet().toArray(); }      finally { r.unlock(); }    }    public Data put(String key, Data value) {      w.lock();      try { return m.put(key, value); }      finally { w.unlock(); }    }    public void clear() {      w.lock();      try { m.clear(); }      finally { w.unlock(); }    }  }

      與普通重入鎖使用的主要區(qū)別在于需要使用不同的鎖對(duì)象引用讀寫鎖,并且在讀寫時(shí)分別調(diào)用對(duì)應(yīng)的鎖。

      ReentrantReadWriteLock鎖實(shí)現(xiàn)分析

      本節(jié)通過(guò)學(xué)習(xí)源碼分析可重入讀寫鎖的實(shí)現(xiàn)。

      圖解重要函數(shù)及對(duì)象關(guān)系

      根據(jù)示例代碼可以發(fā)現(xiàn),讀寫鎖需要關(guān)注的重點(diǎn)函數(shù)為獲取讀鎖及寫鎖的函數(shù),對(duì)于讀鎖及寫鎖對(duì)象則主要關(guān)注加鎖和解鎖函數(shù),這幾個(gè)函數(shù)及對(duì)象關(guān)系如下圖:
      從圖中可見(jiàn)讀寫鎖的加鎖解鎖操作最終都是調(diào)用ReentrantReadWriteLock類的內(nèi)部類Sync提供的方法。與{% post_link 細(xì)談重入鎖ReentrantLock %}一文中描述相似,Sync對(duì)象通過(guò)繼承AbstractQueuedSynchronizer進(jìn)行實(shí)現(xiàn),故后續(xù)分析主要基于Sync類進(jìn)行。

      讀寫鎖Sync結(jié)構(gòu)分析

      Sync繼承于AbstractQueuedSynchronizer,其中主要功能均在AbstractQueuedSynchronizer中完成,其中最重要功能為控制線程獲取鎖失敗后轉(zhuǎn)換為等待狀態(tài)及在滿足一定條件后喚醒等待狀態(tài)的線程。先對(duì)AbstractQueuedSynchronizer進(jìn)行觀察。

      AbstractQueuedSynchronizer圖解

      為了更好理解AbstractQueuedSynchronizer的運(yùn)行機(jī)制,可以首先研究其內(nèi)部數(shù)據(jù)結(jié)構(gòu),如下圖:
      圖中展示AQS類較為重要的數(shù)據(jù)結(jié)構(gòu),包括int類型變量state用于記錄鎖的狀態(tài),繼承自AbstractOwnableSynchronizer類的Thread類型變量exclusiveOwnerThread用于指向當(dāng)前排他的獲取鎖的線程,AbstractQueuedSynchronizer.Node類型的變量headtail。
      其中Node對(duì)象表示當(dāng)前等待鎖的節(jié)點(diǎn),Nodethread變量指向等待的線程,waitStatus表示當(dāng)前等待節(jié)點(diǎn)狀態(tài),mode為節(jié)點(diǎn)類型。多個(gè)節(jié)點(diǎn)之間使用prevnext組成雙向鏈表,參考CLH鎖隊(duì)列的方式進(jìn)行鎖的獲取,但其中與CLH隊(duì)列的重要區(qū)別在于CLH隊(duì)列中后續(xù)節(jié)點(diǎn)需要自旋輪詢前節(jié)點(diǎn)狀態(tài)以確定前置節(jié)點(diǎn)是否已經(jīng)釋放鎖,期間不釋放CPU資源,而AQSNode節(jié)點(diǎn)指向的線程在獲取鎖失敗后調(diào)用LockSupport.park函數(shù)使其進(jìn)入阻塞狀態(tài),讓出CPU資源,故在前置節(jié)點(diǎn)釋放鎖時(shí)需要調(diào)用unparkSuccessor函數(shù)喚醒后繼節(jié)點(diǎn)。
      根據(jù)以上說(shuō)明可得知此上圖圖主要表現(xiàn)當(dāng)前thread0線程獲取了鎖,thread1線程正在等待。

      讀寫鎖Sync對(duì)于AQS使用

      讀寫鎖中Sync類是繼承于AQS,并且主要使用上文介紹的數(shù)據(jù)結(jié)構(gòu)中的statewaitStatus變量進(jìn)行實(shí)現(xiàn)。
      實(shí)現(xiàn)讀寫鎖與實(shí)現(xiàn)普通互斥鎖的主要區(qū)別在于需要分別記錄讀鎖狀態(tài)及寫鎖狀態(tài),并且等待隊(duì)列中需要區(qū)別處理兩種加鎖操作。
      Sync使用state變量同時(shí)記錄讀鎖與寫鎖狀態(tài),將int類型的state變量分為高16位與第16位,高16位記錄讀鎖狀態(tài),低16位記錄寫鎖狀態(tài),如下圖所示:
      Sync使用不同的mode描述等待隊(duì)列中的節(jié)點(diǎn)以區(qū)分讀鎖等待節(jié)點(diǎn)和寫鎖等待節(jié)點(diǎn)。mode取值包括SHAREDEXCLUSIVE兩種,分別代表當(dāng)前等待節(jié)點(diǎn)為讀鎖和寫鎖。

      讀寫鎖Sync代碼過(guò)程分析

      寫鎖加鎖

      通過(guò)對(duì)于重要函數(shù)關(guān)系的分析,寫鎖加鎖最終調(diào)用Sync類的acquire函數(shù)(繼承自AQS

      public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }

      現(xiàn)在分情況圖解分析

      無(wú)鎖狀態(tài)

      無(wú)鎖狀態(tài)AQS內(nèi)部數(shù)據(jù)結(jié)構(gòu)如下圖所示:
      其中state變量為0,表示高位地位地位均為0,沒(méi)有任何鎖,且等待節(jié)點(diǎn)的首尾均指向空(此處特指head節(jié)點(diǎn)沒(méi)有初始化時(shí)),鎖的所有者線程也為空。
      在無(wú)鎖狀態(tài)進(jìn)行加鎖操作,線程調(diào)用acquire 函數(shù),首先使用tryAcquire函數(shù)判斷鎖是否可獲取成功,由于當(dāng)前是無(wú)鎖狀態(tài)必然成功獲取鎖(如果多個(gè)線程同時(shí)進(jìn)入此函數(shù),則有且只有一個(gè)線程可調(diào)用compareAndSetState成功,其他線程轉(zhuǎn)入獲取鎖失敗的流程)。獲取鎖成功后AQS狀態(tài)為:

      有鎖狀態(tài)

      在加寫鎖時(shí)如果當(dāng)前AQS已經(jīng)是有鎖狀態(tài),則需要進(jìn)一步處理。有鎖狀態(tài)主要分為已有寫鎖和已有讀鎖狀態(tài),并且根據(jù)最終當(dāng)前線程是否可直接獲取鎖分為兩種情況:

      1. 非重入:如果滿足一下兩個(gè)條件之一,當(dāng)前線程必須加入等待隊(duì)列(暫不考慮非公平鎖搶占情況)
        a. 已有讀鎖;
        b. 有寫鎖且獲取寫鎖的線程不為當(dāng)前請(qǐng)求鎖的線程。
      2. 重入:有寫鎖且當(dāng)前獲取寫鎖的線程與當(dāng)前請(qǐng)求鎖的線程為同一線程,則直接獲取鎖并將寫鎖狀態(tài)值加1。

      寫鎖重入狀態(tài)如圖:
      寫鎖非重入等待狀態(tài)如圖:
      在非重入狀態(tài),當(dāng)前線程創(chuàng)建等待節(jié)點(diǎn)追加到等待隊(duì)列隊(duì)尾,如果當(dāng)前頭結(jié)點(diǎn)為空,則需要?jiǎng)?chuàng)建一個(gè)默認(rèn)的頭結(jié)點(diǎn)。
      之后再當(dāng)前獲取鎖的線程釋放鎖后,會(huì)喚醒等待中的節(jié)點(diǎn),即為thread1。如果當(dāng)前等待隊(duì)列存在多個(gè)等待節(jié)點(diǎn),由于thread1等待節(jié)點(diǎn)為EXCLUSIVE模式,則只會(huì)喚醒當(dāng)前一個(gè)節(jié)點(diǎn),不會(huì)傳播喚醒信號(hào)。

      讀鎖加鎖

      通過(guò)對(duì)于重要函數(shù)關(guān)系的分析,寫鎖加鎖最終調(diào)用Sync類的acquireShared函數(shù)(繼承自AQS):

      public final void acquireShared(int arg) {        if (tryAcquireShared(arg) < 0)            doAcquireShared(arg);    }

      同上文,現(xiàn)在分情況圖解分析

      無(wú)鎖狀態(tài)

      無(wú)所狀態(tài)AQS內(nèi)部數(shù)據(jù)狀態(tài)圖與寫加鎖是無(wú)鎖狀態(tài)一致:
      在無(wú)鎖狀態(tài)進(jìn)行加鎖操作,線程調(diào)用acquireShared 函數(shù),首先使用tryAcquireShared函數(shù)判斷共享鎖是否可獲取成功,由于當(dāng)前為無(wú)鎖狀態(tài)則獲取鎖一定成功(如果同時(shí)多個(gè)線程在讀鎖進(jìn)行競(jìng)爭(zhēng),則只有一個(gè)線程能夠直接獲取讀鎖,其他線程需要進(jìn)入fullTryAcquireShared函數(shù)繼續(xù)進(jìn)行鎖的獲取,該函數(shù)在后文說(shuō)明)。當(dāng)前線程獲取讀鎖成功后,AQS內(nèi)部結(jié)構(gòu)如圖所示:
      其中有兩個(gè)新的變量:firstReaderfirstReaderHoldCount。firstReader指向在無(wú)鎖狀態(tài)下第一個(gè)獲取讀鎖的線程,firstReaderHoldCount記錄第一個(gè)獲取讀鎖的線程持有當(dāng)前鎖的計(jì)數(shù)(主要用于重入)。

      有鎖狀態(tài)

      無(wú)鎖狀態(tài)獲取讀鎖比較簡(jiǎn)單,在有鎖狀態(tài)則需要分情況討論。其中需要分當(dāng)前被持有的鎖是讀鎖還是寫鎖,并且每種情況需要區(qū)分等待隊(duì)列中是否有等待節(jié)點(diǎn)。

      已有讀鎖且等待隊(duì)列為空

      此狀態(tài)比較簡(jiǎn)單,圖示如:此時(shí)線程申請(qǐng)讀鎖,首先調(diào)用readerShouldBlock函數(shù)進(jìn)行判斷,該函數(shù)根據(jù)當(dāng)前鎖是否為公平鎖判斷規(guī)則稍有不同。如果為非公平鎖,則只需要當(dāng)前第一個(gè)等待節(jié)點(diǎn)不是寫鎖就可以嘗試獲取鎖(考慮第一點(diǎn)為寫鎖主要為了方式寫鎖“餓死”);如果是公平鎖則只要有等待節(jié)點(diǎn)且當(dāng)前鎖不為重入就需要等待。
      由于本節(jié)的前提是等待隊(duì)列為空的情況,故readerShouldBlock函數(shù)一定返回false,則當(dāng)前線程使用CAS對(duì)讀鎖計(jì)數(shù)進(jìn)行增加(同上文,如果同時(shí)多個(gè)線程在讀鎖進(jìn)行競(jìng)爭(zhēng),則只有一個(gè)線程能夠直接獲取讀鎖,其他線程需要進(jìn)入fullTryAcquireShared函數(shù)繼續(xù)進(jìn)行鎖的獲?。?。
      在成功對(duì)讀鎖計(jì)數(shù)器進(jìn)行增加后,當(dāng)前線程需要繼續(xù)對(duì)當(dāng)前線程持有讀鎖的計(jì)數(shù)進(jìn)行增加。此時(shí)分為兩種情況:

      1. 當(dāng)前線程是第一個(gè)獲取讀鎖的線程,此時(shí)由于第一個(gè)獲取讀鎖的線程已經(jīng)通過(guò)firstReaderfirstReaderHoldCount兩個(gè)變量進(jìn)行存儲(chǔ),則僅僅需要將firstReaderHoldCount加1即可;
      2. 當(dāng)前線程不是第一個(gè)獲取讀鎖的線程,則需要使用readHolds進(jìn)行存儲(chǔ),readHoldsThreadLocal的子類,通過(guò)readHolds可獲取當(dāng)前線程對(duì)應(yīng)的HoldCounter類的對(duì)象,該對(duì)象保存了當(dāng)前線程獲取讀鎖的計(jì)數(shù)??紤]程序的局部性原理,又使用cachedHoldCounter緩存最近使用的HoldCounter類的對(duì)象,如在一段時(shí)間內(nèi)只有一個(gè)線程請(qǐng)求讀鎖則可加速對(duì)讀鎖獲取的計(jì)數(shù)。

      第一個(gè)讀鎖線程重入如圖:
      非首節(jié)點(diǎn)獲取讀鎖
      根據(jù)上圖所示,thread0為首節(jié)點(diǎn),thread1線程繼續(xù)申請(qǐng)讀鎖,獲取成功后使用ThreadLocal鏈接的方式進(jìn)行存儲(chǔ)計(jì)數(shù)對(duì)象,并且由于其為最近獲取讀鎖的線程,則cachedHoldCounter對(duì)象設(shè)置指向thread1對(duì)應(yīng)的計(jì)數(shù)對(duì)象。

      已有讀鎖且等待隊(duì)列不為空

      在當(dāng)前鎖已經(jīng)被讀鎖獲取,且等待隊(duì)列不為空的情況下 ,可知等待隊(duì)列的頭結(jié)點(diǎn)一定為寫鎖獲取等待,這是由于在讀寫鎖實(shí)現(xiàn)過(guò)程中,如果某線程獲取了讀鎖,則會(huì)喚醒當(dāng)前等到節(jié)點(diǎn)之后的所有等待模式為SHARED的節(jié)點(diǎn),直到隊(duì)尾或遇到EXCLUSIVE模式的等待節(jié)點(diǎn)(具體實(shí)現(xiàn)函數(shù)為setHeadAndPropagate后續(xù)還會(huì)遇到)。所以可以確定當(dāng)前為讀鎖狀態(tài)其有等待節(jié)點(diǎn)情況下,首節(jié)點(diǎn)一定是寫鎖等待。如圖所示:
      上圖展示當(dāng)前thread0thread1線程獲取讀鎖,thread0為首個(gè)獲取讀鎖的節(jié)點(diǎn),并且thread2線程在等待獲取寫鎖。
      在上圖顯示的狀態(tài)下,無(wú)論公平鎖還是非公平鎖的實(shí)現(xiàn),新的讀鎖加鎖一定會(huì)進(jìn)行排隊(duì),添加等待節(jié)點(diǎn)在寫鎖等待節(jié)點(diǎn)之后,這樣可以防止寫操作的餓死。申請(qǐng)讀鎖后的狀態(tài)如圖所示:
      如圖所示,在當(dāng)前鎖被為讀鎖且有等待隊(duì)列情況下,thread3thread4線程申請(qǐng)讀鎖,則被封裝為等待節(jié)點(diǎn)追加到當(dāng)前等待隊(duì)列后,節(jié)點(diǎn)模式為SHARED,線程使用LockSupport.park函數(shù)進(jìn)入阻塞狀態(tài),讓出CPU資源,直到前驅(qū)的等待節(jié)點(diǎn)完成鎖的獲取和釋放后進(jìn)行喚醒。

      已有寫鎖被獲取

      當(dāng)前線程申請(qǐng)讀鎖時(shí)發(fā)現(xiàn)寫鎖已經(jīng)被獲取,則無(wú)論等待隊(duì)列是否為空,線程一定會(huì)需要加入等待隊(duì)列(注意在非公平鎖實(shí)現(xiàn)且前序沒(méi)有寫鎖申請(qǐng)的等待,線程有機(jī)會(huì)搶占獲取鎖而不進(jìn)入等待隊(duì)列)。寫鎖被獲取的情況下,AQS狀態(tài)為如下?tīng)顟B(tài)
      在兩種情況下,讀鎖獲取都會(huì)進(jìn)入等待隊(duì)列等待前序節(jié)點(diǎn)喚醒,這里不再贅述。

      讀等待節(jié)點(diǎn)被喚醒

      讀寫鎖與單純的排他鎖主要區(qū)別在于讀鎖的共享性,在讀寫鎖實(shí)現(xiàn)中保證讀鎖能夠共享的其中一個(gè)機(jī)制就在于,如果一個(gè)讀鎖等待節(jié)點(diǎn)被喚醒后其會(huì)繼續(xù)喚醒拍在當(dāng)前喚醒節(jié)點(diǎn)之后的SHARED模式等待節(jié)點(diǎn)。查看源碼:

      private void doAcquireShared(int arg) { final Node node = addWaiter(Node.SHARED); boolean failed = true; try { boolean interrupted = false; for (;;) { final Node p = node.predecessor(); if (p == head) { int r = tryAcquireShared(arg); if (r >= 0) { //注意看這里 setHeadAndPropagate(node, r); p.next = null; // help GC if (interrupted) selfInterrupt(); failed = false; return; } } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) cancelAcquire(node); } }

      for循環(huán)中,線程如果獲取讀鎖成功后,需要調(diào)用setHeadAndPropagate方法。查看其源碼:

      private void setHeadAndPropagate(Node node, int propagate) {       Node h = head; // Record old head for check below       setHead(node);       if (propagate > 0 || h == null || h.waitStatus < 0 ||           (h = head) == null || h.waitStatus < 0) {           Node s = node.next;           if (s == null || s.isShared())               doReleaseShared();       }   }

      在滿足傳播條件情況下,獲取讀鎖后繼續(xù)喚醒后續(xù)節(jié)點(diǎn),所以如果當(dāng)前鎖是讀鎖狀態(tài)則等待節(jié)點(diǎn)第一個(gè)節(jié)點(diǎn)一定是寫鎖等待節(jié)點(diǎn)。

      鎖降級(jí)

      鎖降級(jí)算是獲取讀鎖的特例,如在t0線程已經(jīng)獲取寫鎖的情況下,再調(diào)取讀鎖加鎖函數(shù)則可以直接獲取讀鎖,但此時(shí)其他線程仍然無(wú)法獲取讀鎖或?qū)戞i,在t0線程釋放寫鎖后,如果有節(jié)點(diǎn)等待則會(huì)喚醒后續(xù)節(jié)點(diǎn),后續(xù)節(jié)點(diǎn)可見(jiàn)的狀態(tài)為目前有t0線程獲取了讀鎖。
      所降級(jí)有什么應(yīng)用場(chǎng)景呢?引用讀寫鎖中使用示例代碼

      class CachedData { Object data; volatile boolean cacheValid; final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); void processCachedData() { rwl.readLock().lock(); if (!cacheValid) { // Must release read lock before acquiring write lock rwl.readLock().unlock(); rwl.writeLock().lock(); try { // Recheck state because another thread might have // acquired write lock and changed state before we did. if (!cacheValid) { data = ... cacheValid = true; } // Downgrade by acquiring read lock before releasing write lock rwl.readLock().lock(); } finally { rwl.writeLock().unlock(); // Unlock write, still hold read } } try { use(data); } finally { rwl.readLock().unlock(); } } }

      其中針對(duì)變量cacheValid的使用主要過(guò)程為加讀鎖、讀取、釋放讀鎖、加寫鎖、修改值、加讀鎖、釋放寫鎖、使用數(shù)據(jù)、釋放讀鎖。其中后續(xù)幾步(加寫鎖、修改值、加讀鎖、釋放寫鎖、使用數(shù)據(jù)、釋放讀鎖)為典型的鎖降級(jí)。如果不使用鎖降級(jí),則過(guò)程可能有三種情況:

      • 第一種:加寫鎖、修改值、釋放寫鎖、使用數(shù)據(jù),即使用寫鎖修改數(shù)據(jù)后直接使用剛修改的數(shù)據(jù),這樣可能有數(shù)據(jù)的不一致,如當(dāng)前線程釋放寫鎖的同時(shí)其他線程(如t0)獲取寫鎖準(zhǔn)備修改(還沒(méi)有改)cacheValid變量,而當(dāng)前線程卻繼續(xù)運(yùn)行,則當(dāng)前線程讀到的cacheValid變量的值為t0修改前的老數(shù)據(jù);
      • 第二種:加寫鎖、修改值、使用數(shù)據(jù)、釋放寫鎖,即將修改數(shù)據(jù)與再次使用數(shù)據(jù)合二為一,這樣不會(huì)有數(shù)據(jù)的不一致,但是由于混用了讀寫兩個(gè)過(guò)程,以排它鎖的方式使用讀寫鎖,減弱了讀寫鎖讀共享的優(yōu)勢(shì),增加了寫鎖(獨(dú)占鎖)的占用時(shí)間;
      • 第三種:加寫鎖、修改值、釋放寫鎖、加讀鎖、使用數(shù)據(jù)、釋放讀鎖,即使用寫鎖修改數(shù)據(jù)后再請(qǐng)求讀鎖來(lái)使用數(shù)據(jù),這是時(shí)數(shù)據(jù)的一致性是可以得到保證的,但是由于釋放寫鎖和獲取讀鎖之間存在時(shí)間差,則當(dāng)前想成可能會(huì)需要進(jìn)入等待隊(duì)列進(jìn)行等待,可能造成線程的阻塞降低吞吐量。

      因此針對(duì)以上情況提供了鎖的降級(jí)功能,可以在完成數(shù)據(jù)修改后盡快讀取最新的值,且能夠減少寫鎖占用時(shí)間。
      最后注意,讀寫鎖不支持鎖升級(jí),即獲取讀鎖、讀數(shù)據(jù)、獲取寫鎖、釋放讀鎖、釋放寫鎖這個(gè)過(guò)程,因?yàn)樽x鎖為共享鎖,如同時(shí)有多個(gè)線程獲取了讀鎖后有一個(gè)線程進(jìn)行鎖升級(jí)獲取了寫鎖,這會(huì)造成同時(shí)有讀鎖(其他線程)和寫鎖的情況,造成其他線程可能無(wú)法感知新修改的數(shù)據(jù)(此為邏輯性錯(cuò)誤),并且在JAVA讀寫鎖實(shí)現(xiàn)上由于當(dāng)前線程獲取了讀鎖,再次請(qǐng)求寫鎖時(shí)必然會(huì)阻塞而導(dǎo)致后續(xù)釋放讀鎖的方法無(wú)法執(zhí)行,這回造成死鎖(此為功能性錯(cuò)誤)。

      寫鎖釋放鎖過(guò)程

      了解了加鎖過(guò)程后解鎖過(guò)程就非常簡(jiǎn)單,每次調(diào)用解鎖方法都會(huì)減少重入計(jì)數(shù)次數(shù),直到減為0則喚醒后續(xù)第一個(gè)等待節(jié)點(diǎn),如喚醒的后續(xù)節(jié)點(diǎn)為讀等待節(jié)點(diǎn),則后續(xù)節(jié)點(diǎn)會(huì)繼續(xù)傳播喚醒狀態(tài)。

      讀鎖釋放過(guò)程

      讀鎖釋放過(guò)比寫鎖稍微復(fù)雜,因?yàn)槭枪蚕礞i,所以可能會(huì)有多個(gè)線程同時(shí)獲取讀鎖,故在解鎖時(shí)需要做兩件事:

      1. 獲取當(dāng)前線程對(duì)應(yīng)的重入計(jì)數(shù),并進(jìn)行減1,此處天生為線程安全的,不需要特殊處理;
      2. 當(dāng)前讀鎖獲取次數(shù)減1,此處由于可能存在多線程競(jìng)爭(zhēng),故使用自旋CAS進(jìn)行設(shè)置。

      完成以上兩步后,如讀狀態(tài)為0,則喚醒后續(xù)等待節(jié)點(diǎn)。

      總結(jié)

      根據(jù)以上分析,本文主要展示了讀寫鎖的場(chǎng)景及方式,并分析讀寫鎖核心功能(加解鎖)的代碼實(shí)現(xiàn)。Java讀寫鎖同時(shí)附帶了更多其他方法,包括鎖狀態(tài)監(jiān)控和帶超時(shí)機(jī)制的加鎖方法等,本文不在贅述。并且讀寫鎖中寫鎖可使用Conditon機(jī)制也不在詳細(xì)說(shuō)明。

        本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評(píng)論

        發(fā)表

        請(qǐng)遵守用戶 評(píng)論公約

        類似文章 更多