JUC 常見三大輔助類
JUC 常見三大輔助類
CountDownLatch(減少計數)
一個同步輔助類,在完成一組正在其他執行緒中執行的操作之前,它允許一個或多個執行緒一直等待
用給定的計數 初始化 CountDownLatch
,由於呼叫了 countDown()
方法,所以在當前計數到達零之前,await
方法會一直受阻塞;之後,會釋放所有等待的執行緒,await
的所有後續呼叫都將立即返回;這種現象只出現一次——計數無法被重置,如果需要重置計數,請考慮使用 CyclicBarrier
CountDownLatch
是一個通用同步工具,它有很多用途:將計數 1 初始化的 CountDownLatch
用作一個簡單的開/關鎖存器,或入口,在通過呼叫 countDown()
的執行緒開啟入口前,所有呼叫 await
的執行緒都一直在入口處等待;用 N 初始化的 CountDownLatch
可以使一個執行緒在 N 個執行緒完成某項操作之前一直等待,或者使其在某項操作完成 N 次之前一直等待
CountDownLatch 類可以設定一個計數器,然後通過 countDown 方法來進行減 1 的操作,計數器等於 0之前呼叫 await 方法執行緒處於等待狀態直到計數器等於0
總結:
- CountDownLatch 主要有兩個方法,當一個或多個執行緒呼叫 await 方法時,這些執行緒會阻塞
- 其它執行緒呼叫 countDown 方法會將計數器減 1(呼叫 countDown 方法的執行緒不會阻塞)
- 當計數器的值變為 0 時,因 await 方法阻塞的執行緒會被喚醒,繼續執行
程式碼實戰
場景:教室有7 個同學,當所有同學陸續離開教室後值班同學才可以關門
package com.yl.assist; import java.util.concurrent.CountDownLatch; /** * CountDownLatch使用案例 * * @author Y-wee */ public class CountDownLatchTest { public static void main(String[] args) { // 計數器(設定教室剩餘學生數量) CountDownLatch count = new CountDownLatch(7); for (int i = 1; i <= 7; i++) { new Thread(() -> { // 計數器減一(學生離開,教室剩餘學生數量減一) count.countDown(); System.out.println(Thread.currentThread().getName() + "離開了教室"); }, "student" + i).start(); } try { // 計數器等於0(教室剩餘學生數量為0)之前main執行緒等待 count.await(); } catch (InterruptedException e) { e.printStackTrace(); } // 計數器等於0,main執行緒被喚醒(教室剩餘學生數量為0-教室關門) System.out.println("教室關門"); } }
CyclicBarrier(迴圈柵欄)
一個同步輔助類,它允許一組執行緒互相等待,直到到達某個公共屏障點 (common barrier point)
在涉及一組固定大小的執行緒的程式中,這些執行緒必須不時地互相等待,此時 CyclicBarrier 很有用,因為該 barrier 在釋放等待執行緒後可以重用,所以稱它為迴圈 的 barrier
CyclicBarrier 的構造方法第一個引數是目標障礙數,每次執行 CyclicBarrier 一次障礙數會加一,如果達到了目標障礙數,才會執行 cyclicBarrier.await()之後的語句,可以將 CyclicBarrier 理解為加 1 操作
程式碼實戰
場景:集齊 7 顆龍珠就可以召喚神龍
package com.yl.assist;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/**
* CyclicBarrierTest使用案例
*
* @author Y-wee
*/
public class CyclicBarrierTest {
private final static int NUMBER = 7;
public static void main(String[] args) {
/*
建立一個新的 CyclicBarrier,它將在給定數量的參與者(執行緒)處於等待狀態時啟動
並在啟動 barrier 時執行給定的屏障操作,該操作由最後一個進入 barrier 的執行緒執行
(集齊七顆龍珠才可以召喚神龍)
*/
CyclicBarrier cyclicBarrier = new CyclicBarrier(NUMBER,()-> System.out.println("集齊七顆龍珠,召喚神龍成功"));
for (int i = 1; i <= 7; i++) {
new Thread(() -> {
// 模擬收集龍珠
System.out.println(Thread.currentThread().getName()+"集到了");
try {
// 在所有參與者都已經在此 barrier 上呼叫 await 方法之前,將一直等待(龍珠集齊之前處於等待狀態)
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}, "龍珠" + i).start();
}
}
}
Semaphore(訊號燈)
一個計數訊號量,從概念上講,訊號量維護了一個許可集,如有必要,在許可可用前會阻塞每一個 acquire()
,然後再獲取該許可,每個 release()
釋放一個許可,從而可能釋放一個正在阻塞的獲取者;Semaphore 只對可用許可的號碼進行計數,並採取相應的行動
Semaphore 通常用於限制可以訪問某些資源(物理或邏輯的)的執行緒數目
總結:Semaphore維護了一個許可集,當執行緒未獲取到許可時處於等待狀態,直到執行緒獲取到許可
程式碼實戰
場景:搶車位,7 部汽車 3 個停車位
package com.yl.assist;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
/**
* Semaphore使用案例
*
* @author Y-wee
*/
public class SemaphoreTest {
public static void main(String[] args) {
// 設定訊號量(許可集),模擬三個停車位
Semaphore semaphore = new Semaphore(3);
for (int i = 1; i <= 7; i++) {
new Thread(() -> {
try {
// 獲取訊號量(停車許可)
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "進入了停車位");
// 執行緒等待(停車3s)
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
/*
注意:離開停車位的輸出語句需要寫在釋放訊號量之前,不然可能造成訊號量(停車許可釋放了),
但是車子還沒有離開,此時,另一個執行緒(車子)獲取到了停車許可進來了
*/
System.out.println(Thread.currentThread().getName() + "離開了停車位");
// 釋放訊號量(停車許可)
semaphore.release();
}
}, "車輛" + i).start();
}
}
}