常用并发工具类(锁和线程间通信工具类)
常用并發工具類總結
JUC 下的常用并發工具類(鎖和線程間通信工具類),主要包括 ReentrantLock、ReentrantReadWriteLock、CountDownLatch、CyclicBarrier、Semaphore、Exchanger
ReentrantLock 和 ReentrantReadWriteLock
ReentrantLock 是可重入獨占鎖,ReentrantReadWriteLock 是可重入讀寫鎖。
ReentrantLock
ReentrantLock 支持公平和非公平鎖兩種模式,可以通過 new ReentrantLock(boolean fair) 構造方法來指定公平或非公平模式,默認為非公平模式。
注意,公平和非公平模式在這里主要指的是嘗試獲取鎖的線程是否會在排到同步隊列末尾之前嘗試獲取一遍鎖。
- 如果在排到隊尾之前會嘗試獲取一邊鎖,那么就是非公平模式,因為有可能在這一次嘗試獲取就直接得到了鎖,等同于一個插隊操作,所以是非公平鎖
- 如果是老老實實排到隊尾,那么就是公平模式,因為不會存在插隊操作
ReentrantLock 的公平與非公平模式,主要是靠其內部的兩個 AQS 子類,FairSync 和 NonfairSync 來實現的。
RenntrantReadWriteLock
ReentrantReadWriteLock 是讀寫鎖,其主要性質如下:
- ReentrantReadWriteLock 內部有一個 Sync 對象,用于執行所有的同步機制
- Sync 類繼承了 AQS
- Sync 類有一個公平鎖實現 FairSync,和一個非公平鎖實現 NonfairSync
- Sync 類中 state 的含義為,高 16 位代表獲取讀鎖的次數,低 16 位代表獲取寫鎖的次數
- 內部有一個 ReadLock 對象(讀鎖),有一個 WriteLock 對象(寫鎖)
- 讀鎖和寫鎖實際上都是依賴于 Sync 對象來實現的
- 基于 Sync 對象對于 state 的定義,讀鎖和寫鎖的獲取次數上限都為 65535 (2^16-1)
- 讀鎖的加鎖操作本質上是對 state 的高 16 位進行 +1 操作,釋放鎖本質上是對 state 的高 16 位進行 -1 操作
- 寫鎖的加鎖操作本質上是對 state 的低 16 位進行 +1 操作,釋放鎖本之上是對 state 的低 16 位進行 -1 操作
- 操作順序與互斥關系
- 讀讀不互斥
- 寫寫互斥
- 讀寫互斥
- 寫讀互斥
- 讀鎖和寫鎖均可重入
- 讀鎖不能升級為寫鎖(即持有讀鎖時去獲取寫鎖)
- 如果使用 writeLock().lock() 獲取寫鎖會發生死鎖
- 如果使用 writeLock().tryLock(long timeout, Timeunit unit) 會在超時后返回
- 寫鎖可以降級為讀鎖(即持有寫鎖時去獲取讀鎖)
- 讀鎖和寫鎖的加鎖和釋放鎖的邏輯必須成對出現
Semaphore
Semaphore 是信號量的意思,主要用于限定臨界資源同時能被多少個線程訪問。主要特性:
- 內部有一個 Sync 類繼承了 AQS,是 AQS 共享模式的一種具體實現
- Sync 類有一個公平鎖實現 FairSync,和一個非公平鎖實現 NonfairSync,由構造函數 public Semaphore(int permits, boolean fair) 來指定
- 公平鎖實現,當前線程獲取不到許可時,將會進入隊列等待
- 非公平鎖實現,當前線程將會一直(自旋)嘗試獲取許可
- Sync 類中 state 的含義為 public Semaphore(int permits) 中 permits 的數值(許可的數量)
- Sync 類的獲取許可操作本質上是對 state 的 -1 操作,釋放許可操作本之上是對 state 的 +1 操作
Exchanger
Exchanger 是交換器的意思,主要用于兩個線程之間的數據交換。
注意在多個線程同時使用一個 Exchanger 對象進行數據交換的時候,交換結果可能是未知的。所以 Exchanger 適用于成對線程之間的數據通信。
CyclicBarrier
CyclicBarrier 是循環柵欄的意思,主要用于幫助多個線程同時達到某個等待點后再把這些線程喚醒。主要特性:
- 主要的構造函數為 public CyclicBarrier(int parties) 和 public CyclicBarrier(int parties, Runnable barrierAction)
- parties 代表的是參與協作的線程數量
- barrierAction 代表的是協作線程的數量等于 parties 時,將會執行的動作
- CyclicBarrier 有一把內置的 ReentrantLock 鎖,和由這把鎖創建出來的一個 Condition 對象
- 當調用 await() 方法時,首先會嘗試獲取鎖,獲取成功后當前需要等待的剩余協作線程數量 -1
- 如果此時需要等待的剩余協作線程數量不為 0,即代表參與協作的線程數量還沒有到達初始設定的數量,則調用 Condition 對象的 await() 方法,將當前線程阻塞在 Condition 對象的條件隊列中
- 如果此時需要等待的協作線程數量為 0,即代表參與協作的線程數量已經到達了初始設定的數量,則執行打開柵欄動作(調用 nextGeneration() 方法),(調用 Condition 對象的 singalAll() 方法)喚醒所有等待在 Condition 對象上的線程,喚醒完成后,重置柵欄到初始狀態
- 當調用 reset() 方法時,之前調用了 await() 方法的所有線程將會被喚醒,并拋出異常
- 當某個線程調用了 await() 方法后被中斷,那么 CyclicBarrier 會被設置成一個中斷狀態,此時需要調用 reset() 方法把 CyclicBarrier 對象重置成初始狀態
總的來說 ,CyclicBarrier 是通過 AQS 的條件等待機制(ConditionObject)來實現的
CountDownLatch
CountDownLatch 是計數門閂的意思,主要用于在一組執行線程都到達某個等待點之后,再喚醒等待在這個門閂上的所有線程一個同步工具。
- 內部有一個 Sync 類繼承了 AQS,是 AQS 共享模式的一種具體實現
- Sync 類中 state 的含義為 public CountDownLatch(int count) 中 count 的數值(需要打開門閂的次數)
- 調用 countDown() 方法后(相當于嘗試釋放共享鎖),將會使用 CAS + 自旋將 state 變量值 -1
- 調用 await() 方法后(相當于嘗試獲取共享鎖),首先會判斷當前的 state 是否等于 0
- 如果等于 0,則獲取成功,將會立馬返回
- 如果不等于 0,則獲取失敗,當前線程將會被封裝為共享節點放入到 AQS 同步隊列中,并阻塞
CyclicBarrier 和 CountDownLatch 的異同
相同點:
- 都是 JUC 包下的同步工具類
- 都是 AQS 的應用實例
- 都可以在指定數目個線程到達某個等待點后,執行一些動作
不同點:
- 實現目標不同:
- CyclicBarrier 是幫助多個協作線程最終都到達某個等待點后,再同時喚醒這些協作線程本身
- CountDownLatch 是在多個執行線程最終都到達某個等待點后,再同時喚醒所有等待線程
- 調用方式不同:
- CyclicBarrier 調用 await() 方法后,如果此時的 count 值不為 0,那么將會阻塞自身,所以不能在同一時間連續調用
- CountDownLatch 在調用 countDown() 方法后,不會阻塞自身,在同一時間可以連續調用
- 阻塞、修改計數計數和通知的對象不一樣:
- CyclicBarrier 阻塞、修改計數和通知的對象都是執行 await() 方法的線程
- CountDownLatch 阻塞和通知的是調用 await() 方法的線程,修改計數的對象是執行 countDown() 方法的線程
- 實現方式不同:
- CyclicBarrier 是基于 AQS 的條件等待機制來實現的
- CountDownLatch 內部有一個 Sync 類直接繼承了 AQS,是 AQS 共享機制的一種實現
- 狀態是否可重置:
- CyclicBarrier 可以在到達計數條件后重置自身狀態,可以重復使用;也可以主動調用 reset() 方法來重置自身狀態
- CountDownLatch 不能重置自身狀態,不能重復使用
- 計數條件到達后是否可以執行操作:
- CyclicBarrier 可以調用 public CyclicBarrier(int parties, Runnable barrierAction) 中傳入計數條件到達后的操作。在計數條件到達之后,將會調用 barrierAction 的 run() 方法執行一些特殊操作
- CountDownLatch 不能設置計數條件到達后的執行操作
總結
以上是生活随笔為你收集整理的常用并发工具类(锁和线程间通信工具类)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java system_深入分析java
- 下一篇: java使用localstorage_s