JUC之ContDownLatch CyclicBarrier Semaphore使用記錄
阿新 • • 發佈:2021-01-15
水平有限,只記錄使用方法和個人理解,不保證全對
閒聊
- 第一次學習到JUC時候應該是19年,JUC包下的AQS和CAS 併發容器 相關的類都瞭解了一遍。當時想記錄下來但是又覺得理解不夠深刻。日常工作中又很少會用到相關的知識。所以一直擱淺到現在。中間陸陸續續遺忘又撿起。
- 慢慢的發現如果只有學習沒有產出,學習的意義好像就是當你遇到一個場景會想到有這種解決方案,然後繼續去找這方面的東西重新補充。
- 所以試著以當下的狀態去敘述這些東西,也算是以後再拿起的索引
ContDownLatch
- ContDownLatch是一個同步輔助類,用於阻塞一個或者多個執行緒的工具類。多用於阻塞當前主執行緒執行,等待字執行緒的執行結果。
- 當所有執行緒準備就緒,才開始執行
- 建立時候指定初始值,每一個執行緒執行完後呼叫countDown方法來表述自己任務執行完畢,所有執行緒執行完之後,被await方法阻塞的執行緒開始繼續執行
- 1+2+3+4 的計算,同時兩個執行緒去計算一個執行緒執行1+2 另一個執行緒執行 3+ 4 再將子執行緒的結果累加,所以主執行緒是需要子執行緒的執行結果,需要等子執行緒都執行完畢之後在繼續執行的。
- 比如打英雄聯盟的時候十個人都準備好了,然後就開始團戰。
public class CountDownLatchDemo { private static final int THREAD_COUNT = 10; private static final CountDownLatch countDownLatch = new CountDownLatch(THREAD_COUNT); private static final ExecutorService executorService = ThreadUtil.newExecutor(THREAD_COUNT, THREAD_COUNT * 2); public static void main(String[] args) throws Exception { for (int i = 0; i < 10; i++) { final int threadNum = i; executorService.execute(() -> { try { ready(threadNum); } catch (Exception e) { e.printStackTrace(); } }); } countDownLatch.await();// 主執行緒阻塞,等待所有子執行緒到達屏障 executorService.shutdown(); System.out.println("開打!開打!"); } private static void ready(int threadNum) throws Exception { Thread.sleep(1000);// 補充狀態,技能冷卻 System.out.println("英雄" + threadNum + "準備就緒"); countDownLatch.countDown();// 執行緒到達屏障 } }
CyclicBarrier
- CyclicBarrier和CountDownLatch 有很多相似之處,首先都是基於計數器實現,但是有著比CountDownLatch 更多的應用場景和更強大的功能。
- 可以使多個執行緒之間相互等待,只有等所有的執行緒都執行完畢之後才會繼續往下執行
- 當呼叫await()時執行緒會進入等待,等待的執行緒數到達我們設定的初始值後,執行緒繼續執行
- 當CyclicBarrier釋放執行緒後可以重用所以也稱之為可迴圈柵格
- 玩過英雄聯盟的人應該都知道出門慢輸一半,所以一波公平的團戰,應該等大家都準備就緒才開始,這才是競技精神。
- 所以正常的團戰邏輯應該是大家回家補充狀態,等待技能冷卻,然後回到中路,開始團戰,活下來的人才有資格補刀。
public class CyclicBarrierDemo {
private static final int THREAD_COUNT = 10;
// 十個英雄,主執行緒需要等待所有子執行緒所以 10 + 1
private final static CyclicBarrier cyclicBarrier = new CyclicBarrier(11);
private static final ExecutorService executorService = ThreadUtil.newExecutor(THREAD_COUNT, THREAD_COUNT * 2);
public static void main(String[] args) throws Exception {
for (int a = 0; a < 2; a++) {
for (int i = 0; i < 10; i++) {
final int threadNum = i;
final int threadCou = a;
executorService.execute(() -> {
try {
ready(threadNum, threadCou);
} catch (Exception e) {
e.printStackTrace();
}
});
}
// 需要等所有的英雄都準備就緒
cyclicBarrier.await();
if(a == 0){
System.out.println("所有英雄準備就緒,準備出發");
}
}
System.out.println("開打!開打!");
executorService.shutdown();
}
private static void ready(int threadNum, int threadCou) throws Exception {
Thread.sleep(1000);// 補充狀態,技能冷卻
if(threadCou == 0){
System.out.println("英雄" + threadNum + "準備就緒");
}else {
System.out.println("英雄" + threadNum + "到達戰場");
}
cyclicBarrier.await();
}
}
Semaphore
- Semaphore可以控制一個資源可以同時被訪問的次數。
- 比如在資料庫連線池中的時候用
public class SemaphoreDemo {
private static final int THREAD_COUNT = 4;
private static final Semaphore semaphore = new Semaphore(2);
private static final ExecutorService executorService = ThreadUtil.newExecutor(THREAD_COUNT, THREAD_COUNT * 2);
public static void main(String[] args) {
for (int i = 0; i < 4; i++) {
final int threadNum = i;
executorService.execute(() -> {
try {
if (semaphore.availablePermits() == 0) {
System.out.println(threadNum + "----->> 等待獲取連線");
}
semaphore.acquire();// 獲取憑證
ready(threadNum);
} catch (Exception e) {
e.printStackTrace();
}
});
}
executorService.shutdown();
}
private static void ready(int threadNum) throws Exception {
System.out.println(threadNum + "連線被獲取");
Thread.sleep(2000); // sql執行需要時間
System.out.println("sql執行完畢" + threadNum + "歸還");
semaphore.release();// 釋放憑證
}
}