ReentrantLock-互斥同步器

‍ReentrantLock是一個互斥的同步器,其實現了接口Lock,裏面的功能函數主要有:
1. ‍lock() -- 阻塞模式獲取資源
2. ‍lockInterruptibly() -- 可中斷模式獲取資源
3. ‍tryLock() -- 嘗試獲取資源
4. tryLock(time) -- 在一段時間內嘗試獲取資源
5. ‍unlock() -- 釋放資源

ReentrantLock實現Lock有兩種模式即公平模式和不公平模式
Concurrent包下的同步器都是基於AQS框架,在ReentrantLock裏面會看到這樣三個類
-----------------------------------------------------------------------
static abstract class Sync extends AbstractQueuedSynchronizer {
    abstract void lock();
    final boolean nonfairTryAcquire(int acquires) { ... }
    protected final boolean tryRelease(int releases) { ... }
}
-----------------------------------------------------------------------
final static class NonfairSync extends Sync {
    protected final boolean tryAcquire(int acquires) { ... }
    final void lock() { ... }
}
-----------------------------------------------------------------------
final static class FairSync extends Sync {
    final void lock() { ... }
    protected final boolean tryAcquire(int acquires) { ... }
}
-----------------------------------------------------------------------
再回歸到ReentrantLock對Lock的實現上
0. ‍ReentrantLock實例化
   ReentrantLock有個屬性sync,實際上對Lock接口的實現都是包裝了一下這個sync的實現
   如果是公平模式則創建一個FairSync對象,否則創建一個NonfairSync對象,默認是不公平模式
1. lock() 調用sync.lock()
   公平模式下:直接走AQS的acquire函數,此函數的邏輯走一次tryAcquire,如果成功
   線程拜託同步器的控制,否則加入NODE鏈表,進入acquireQueued的tryAcquire,休眠,被喚醒的輪迴
   不公平模式下和公平模式下邏輯大體上是一樣的,不同點有兩個:
   a. 在執行tryAcquire之前的操作,不公平模式會直接compareAndSetState(0, 1)原子性的設置AQS的資源
   0表示目前沒有線程佔據資源,則直接搶佔資源,不管AQS的NODE鏈表的FIFO原則
   b. tryAcquire的原理不一樣,不公平模式的tryAcquire只看compareAndSetState(0, 1)能否成功
   而公平模式還會加一個條件就是此線程對於的NODE是不是NODE鏈表的第一個
   c. 由於tryAcquire的實現不一樣,而公平模式和不公平模式在lock期間走的邏輯是一樣的(AQS的acquireQueued的邏輯)
   d. 對於一個線程在獲取到資源後再調用lock會導致AQS的資源做累加操作,同理線程要徹底的釋放資源就必須同樣
   次數的調用unlock來做對應的累減操作,因爲對應ReentrantLock來說tryAcquire成功一個必須的條件就是compareAndSetState(0, 1)
   e. 由於acquireQueued過程中屏蔽了線程中斷,只是在線程拜託同步器控制後,如果記錄線程在此期間被中斷過則標記線程的
   中斷狀態
2. ‍lockInterruptibly() 調用sync.acquireInterruptibly(1),上一篇文章講過AQS的核心函數,這個過程和acquireQueued
   是一樣的,只不過在阻塞期間如果被標記中斷則線程在park期間被喚醒,然後直接退出那個輪迴,拋出中斷異常
   由於公平模式和不公平模式下對tryAcquire的實現不一樣導致‍lockInterruptibly邏輯也是不一樣
3. tryLock() 函數只是嘗試性的去獲取一下鎖,跟tryAcquire一樣,這兩種模式下走的代碼一樣都是公平模式下的代碼
4. tryLock(time) 調用sync.tryAcquireNanos(time),上一篇文章講過AQS的核心函數,這個過程和acquireQueued一樣,
   a. 在阻塞前會先計算阻塞的時間,進入休眠
   b. 如果被中斷則會判斷時間是否到了
      1. 如果沒到則且被其他線程設置了中斷標誌,退出那個輪迴,拋出中斷異常,如果沒有被設置中斷標記則是前一個線程
      釋放了資源再喚醒了它,其繼續走那個輪迴,輪迴中,如果tryAcquire成功則擺脫了同步器的控制,否則回到a
      2. 如果時間到了則退出輪迴,獲取資源失敗
5. ‍unlock() 調用sync.release(1),上一篇文章講過AQS的核心函數,release函數會調用Sync實現的tryRelease函數來判斷
   釋放資源是否成功,即Sync.tryRelease函數,其邏輯過程是
   a. 首先判斷目前佔據資源的線程是不是調用者,如果不是會拋出異常IllegalMonitorStateException
   b. 如果是則進行AQS資源的減1邏輯,如果再減1後AQS資源變成0則表示調用線程測得放棄了此鎖,返回給release的值的TRUE,
   release會喚醒下一個線程
-----------------------------------------------------------------------
整體來看ReentrantLock互斥鎖的實現大致是
1. 自己實現AQS的tryAcquire和tryRelease邏輯,tryAcquire表示嘗試去獲取鎖,tryRelease表示嘗試去釋放鎖
2. ReentrantLock對lock(),trylock(),trylock(time),unlock()的實現都是使用AQS的框架,然後AQS的框架又返回調用
ReentrantLock實現的tryAcquire和tryRelease來對線程是否獲取鎖和釋放鎖成功做出依據判斷

發佈了45 篇原創文章 · 獲贊 24 · 訪問量 25萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章