Java併發 AQS架構

1. LockSupport類
AQS架構使用LockSupport來實現線程的休眠,時間性休眠,喚醒...

2. ‍AQS架構-所有同步器的基本架構
----- a. 基於‍‍AQS架構的同步器基本上只需要實現四個函數
          1. tryAcqurie(int) -- 線程會調用這個函數來做能否互斥的獲取同步器資源的嘗試的邏輯
          2. tryRelease(int) -- ‍線程會調用這個函數來做能否互斥的釋放同步器資源的嘗試的邏輯
          3. tryAcqurieShared(int) -- ‍線程會調用這個函數來做能否共享的獲取同步器資源的嘗試的邏輯
          ‍4. tryReleaseShared(int) -- ‍ ‍線程會調用這個函數來做能否共享的釋放同步器資源的嘗試的邏輯

----- b. 基於AQS架構同步器分爲
          互斥同步器 例如‍ReentrantLock
          共享同步器 例如‍Semaphore
          互斥和共享共存的同步器‍ReentrantReadWriteLock
          接下來會有文章依次介紹這些同步器的實現

----- c. AQS數據結構
         1. 對於首次進入時獲取資源失敗的Thread,會將其封裝到一個NODE數據塊裏面
         2. 會維護兩個NODE引用,分別指向NODE雙向鏈表的頭和尾,這樣來實現FIFO
         3. 對於同步器所擁有的資源就是使用一個INT來表示,這也就是‍互斥和共享共存的
         同步器‍同時共享的線程只能是2個BYTE能表示的長度的原因,所有又推出了一個LONG型的AQS架構

----- d. AQS核心代碼
          1. acquireQueued() 實現了線程不可中斷模式下的互斥獲取資源
          其主要邏輯是一個死循環
              a. 線程所屬的NODE->prev是HEAD纔會調用‍tryAcqurie執行嘗試獲取鎖的邏輯
              b. 如果嘗試獲取鎖成功則head=NODE,以及一個釋放HARD REFERENCE的操作
              c. 返回線程在WAIT期間是否被標記中斷 ---> 線程擺脫同步器的控制
              d. 對‍‍tryAcqurie嘗試失敗後是否需要WAIT做出判斷 ---- 重點處
                  1. 如果前一個NODE處於WAIT則它一定要WAIT
                  2. 如果前一個NODE處於CANCEL則做一個鏈表的刪除工作
                  3. 如果前一個NODE是最後加入的則將其標記爲WAIT
                  4. 2和3會讓線程再做一次嘗試,不會阻塞線程
              e. 如果線程需要阻塞則調用‍LockSupport.park阻塞,兩種方式會讓線程醒來
                  1. 線程被標記中斷
                  2. 別的線程‍用‍LockSupport.unpark了它,有線程釋放了互斥資源
              f. 線程醒來循環到a
              g. 程序的邏輯就是一個嘗試獲取資源,線程阻塞,被喚醒的輪迴

          2. ‍doAcquireInterruptibly()‍實現了線程可中斷模式下的互斥獲取資源
          其原理和‍acquireQueued()一樣,不同之處是線程在被中斷後即在1.e.1條件下會直接退出死循環

          3. ‍doAcquireNanos‍()‍實現了線程可中斷+記時模式下的互斥獲取資源              
               a. 在阻塞前會先計算阻塞的時間,進入休眠
               b. 如果被中斷則會判斷時間是否到了
                  1. 如果沒到則且被其他線程設置了中斷標誌,退出那個輪迴,拋出中斷異常,如果沒有被設置中斷標記則是前一個線程
                  釋放了資源再喚醒了它,其繼續走那個輪迴,輪迴中,如果tryAcquire成功則擺脫了同步器的控制,否則回到a
                  2. 如果時間到了則退出輪迴,獲取資源失敗

          4. release()實現了互斥資源的釋放,最主要的工作就是將其下一個線程喚醒‍1.e.2

          5. doAcquireShared()‍實現了線程不可中斷模式下的共享獲取資源
               a. 線程所屬的NODE->prev是HEAD纔會調用‍‍tryAcquireShared執行嘗試獲取鎖的邏輯
               b. 如果嘗試獲取鎖成功則head=NODE,以及一個釋放HARD REFERENCE的操作
               此處和互斥的明顯區別是,線程在獲取成功後如果還有剩餘的資源會喚醒其後的線程
               也就是如果其下一個線程也是共享的獲取資源,其被喚醒的理由多了個
               c. 線程在WAIT期間是否被標記中斷則標記,因爲這期間的標記狀態會被清掉 ---> 線程擺脫同步器的控制
               d. 對‍‍tryAcquireShared嘗試失敗後是否需要WAIT做出判斷 ---- 重點處
                  1. 如果前一個NODE處於WAIT則它一定要WAIT
                  2. 如果前一個NODE處於CANCEL則做一個鏈表的刪除工作
                  3. 如果前一個NODE是最後加入的則將其標記爲WAIT
                  4. 2和3會讓線程再做一次嘗試,不會阻塞線程
               e. 如果線程需要阻塞則調用‍LockSupport.park阻塞,三種方式會讓線程醒來
                   1. 線程被標記中斷
                   2. 別的線程‍釋放了資源LockSupport.unpark了它
                   3. 其前一個共享線程醒了以後獲取了資源而其還有資源也會喚醒它
               f. 線程醒來循環到a
               g. 程序的邏輯就是一個嘗試獲取資源,線程阻塞,被喚醒的輪迴

          6. ‍doAcquireSharedInterruptibly()‍實現了線程可中斷模式下的共享獲取資源
          其原理和‍‍doAcquireShared()一樣,不同之處是線程在被中斷後即在5.e.1條件下會直接退出死循環

          7. doAcquireSharedNanos()‍實現了線程可中斷+記時模式下的共享獲取資源
               a. 在阻塞前會先計算阻塞的時間,進入休眠
               b. 如果被中斷則會判斷時間是否到了
                  1. 如果沒到則且被其他線程設置了中斷標誌,退出那個輪迴,拋出中斷異常,如果沒有被設置中斷標記則是前一個線程
                  釋放了資源再喚醒了它,其繼續走那個輪迴,輪迴中,如果tryAcquire成功則擺脫了同步器的控制,否則回到a
                  2. 如果時間到了則退出輪迴,獲取資源失敗

           8. ‍releaseShared()和‍release()一樣

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章