什么是并發(fā)流程控制
- 控制并發(fā)流程的工具類,作用就是幫助我們程序員更容易的讓線程之間進(jìn)行合作
- 讓線程之間相互配合,來(lái)滿足業(yè)務(wù)需求
- 比如,讓線程A等待線程B執(zhí)行完畢后在執(zhí)行等合作策略
常見(jiàn)的控制并發(fā)流程工具類

CountDownLatch
CountDownLatch是一個(gè)同步工具類,用來(lái)協(xié)調(diào)多個(gè)線程之間的同步,或者說(shuō)起到線程之間的通信(而不是用作互斥的作用)。
CountDownLatch能夠使一個(gè)線程在等待另外一些線程完成各自工作之后,再繼續(xù)執(zhí)行。使用一個(gè)計(jì)數(shù)器進(jìn)行實(shí)現(xiàn)。計(jì)數(shù)器初始值為線程的數(shù)量。當(dāng)每一個(gè)線程完成自己任務(wù)后,計(jì)數(shù)器的值就會(huì)減一。當(dāng)計(jì)數(shù)器的值為0時(shí),表示所有的線程都已經(jīng)完成一些任務(wù),然后在CountDownLatch上等待的線程就可以恢復(fù)執(zhí)行接下來(lái)的任務(wù)。

CountDownLatch的主要方法
- public CountDownLatch(int count):僅有一個(gè)構(gòu)造函數(shù),參數(shù)count為需要倒數(shù)的數(shù)值
- await():調(diào)用await()方法的線程會(huì)被掛起,它會(huì)等待直到count值為0才繼續(xù)執(zhí)行
- public void countDown():將count值減1,直到為0時(shí),等待的線程會(huì)被喚醒
圖解await和countDown方法

CountDownLatch的用法
- CountDownLatch典型用法:1、某一線程在開(kāi)始運(yùn)行前等待n個(gè)線程執(zhí)行完畢。將CountDownLatch的計(jì)數(shù)器初始化為new CountDownLatch(n),每當(dāng)一個(gè)任務(wù)線程執(zhí)行完畢,就將計(jì)數(shù)器減1 countdownLatch.countDown(),當(dāng)計(jì)數(shù)器的值變?yōu)?時(shí),在CountDownLatch上await()的線程就會(huì)被喚醒。一個(gè)典型應(yīng)用場(chǎng)景就是啟動(dòng)一個(gè)服務(wù)時(shí),主線程需要等待多個(gè)組件加載完畢,之后再繼續(xù)執(zhí)行。
/**
* @Description: 工廠中,質(zhì)檢,5個(gè)工人檢查,所有人都認(rèn)為通過(guò),才通過(guò)
*/
public class CountDownLatchDemo1 {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(5);
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++) {
final int no = i+1;
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
Thread.sleep(new Random().nextInt(10000));
System.out.println("NO." + no + "完成了檢查");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
countDownLatch.countDown();
}
}
};
executorService.submit(runnable);
}
System.out.println("等待5個(gè)人檢查完......");
countDownLatch.await();
System.out.println("所有人都完成了工作,等待進(jìn)入下一環(huán)節(jié)");
}
}
- CountDownLatch典型用法:2、實(shí)現(xiàn)多個(gè)線程開(kāi)始執(zhí)行任務(wù)的最大并行性。注意是并行性,不是并發(fā),強(qiáng)調(diào)的是多個(gè)線程在某一時(shí)刻同時(shí)開(kāi)始執(zhí)行。類似于賽跑,將多個(gè)線程放到起點(diǎn),等待發(fā)令槍響,然后同時(shí)開(kāi)跑。做法是初始化一個(gè)共享的CountDownLatch(1),將其計(jì)算器初始化為1,多個(gè)線程在開(kāi)始執(zhí)行任務(wù)前首先countdownlatch.await(),當(dāng)主線程調(diào)用countDown()時(shí),計(jì)數(shù)器變?yōu)?,多個(gè)線程同時(shí)被喚醒。
/**
* @Description: 模擬100米跑步,5名選手都準(zhǔn)備好了,只等裁判員一聲令下,所有人同時(shí)開(kāi)跑
*/
public class CountDownLatchDemo2 {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(1);
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++) {
final int no = i+1;
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("No." + no + ",準(zhǔn)備完畢,等待發(fā)令槍");
try {
countDownLatch.await();
System.out.println("No." + no + ",開(kāi)始跑步");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
executorService.submit(runnable);
}
//裁判員檢查發(fā)令槍......
Thread.sleep(5000);
System.out.println("發(fā)令槍響,比賽開(kāi)始......");
countDownLatch.countDown();
}
}
CountDownLatch兩種用法結(jié)合使用
/**
* @Description: 模擬100米跑步,5名選手都準(zhǔn)備好了,只等裁判員一聲令下,所有人同時(shí)開(kāi)跑
* 當(dāng)所有人都到終點(diǎn)后,比賽結(jié)束
*/
public class CountDownLatchDemo3 {
public static void main(String[] args) throws InterruptedException {
CountDownLatch begin = new CountDownLatch(1);
CountDownLatch end = new CountDownLatch(5);
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++) {
final int no = i+1;
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("No." + no + ",準(zhǔn)備完畢,等待發(fā)令槍");
try {
begin.await();
System.out.println("No." + no + ",開(kāi)始跑步");
Thread.sleep(new Random().nextInt(10000));
System.out.println("No." + no + ",跑到終點(diǎn)了");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
end.countDown();
}
}
};
executorService.submit(runnable);
}
//裁判員檢查發(fā)令槍......
Thread.sleep(5000);
System.out.println("發(fā)令槍響,比賽開(kāi)始......");
begin.countDown();
//等待5個(gè)線程都執(zhí)行完畢之后
end.await();
System.out.println("所有人到達(dá)終點(diǎn),比賽結(jié)束");
}
}
CountDownLatch注意點(diǎn)
- 擴(kuò)展用法:多個(gè)線程等待多個(gè)線程完成執(zhí)行后,再同時(shí)執(zhí)行
- CountDownLatch是不能夠重用的,如果需要重新計(jì)數(shù),可以考慮使用CyclicBarrier或者創(chuàng)建新的CountDownLatch實(shí)例
Semaphore
Semaphore是一種在多線程環(huán)境下使用的設(shè)施,該設(shè)施負(fù)責(zé)協(xié)調(diào)各個(gè)線程,以保證它們能夠正確、合理的使用公共資源的設(shè)施,也是操作系統(tǒng)中用于控制進(jìn)程同步互斥的量。Semaphore是一種計(jì)數(shù)信號(hào)量,用于管理一組資源,內(nèi)部是基于AQS的共享模式。它相當(dāng)于給線程規(guī)定一個(gè)量從而控制允許活動(dòng)的線程數(shù)。
Semaphore(信號(hào)量)是用來(lái)控制同時(shí)訪問(wèn)特定資源的線程數(shù)量,它通過(guò)協(xié)調(diào)各個(gè)線程,以保證合理的使用公共資源。很多年以來(lái),我都覺(jué)得從字面上很難理解Semaphore所表達(dá)的含義,只能把它比作是控制流量的紅綠燈,比如XX馬路要限制流量,只允許同時(shí)有一百輛車在這條路上行使,其他的都必須在路口等待,所以前一百輛車會(huì)看到綠燈,可以開(kāi)進(jìn)這條馬路,后面的車會(huì)看到紅燈,不能駛?cè)隭X馬路,但是如果前一百輛中有五輛車已經(jīng)離開(kāi)了XX馬路,那么后面就允許有5輛車駛?cè)腭R路,這個(gè)例子里說(shuō)的車就是線程,駛?cè)腭R路就表示線程在執(zhí)行,離開(kāi)馬路就表示線程執(zhí)行完成,看見(jiàn)紅燈就表示線程被阻塞,不能執(zhí)行。
Semaphore 是 synchronized 的加強(qiáng)版,作用是控制線程的并發(fā)數(shù)量。就這一點(diǎn)而言,單純的synchronized 關(guān)鍵字是實(shí)現(xiàn)不了的。

Semaphore主要方法:
- Semaphore(int permits):構(gòu)造方法,創(chuàng)建具有給定許可數(shù)的計(jì)數(shù)信號(hào)量并設(shè)置為非公平信號(hào)量。
- Semaphore(int permits,boolean fair):構(gòu)造方法,當(dāng)fair等于true時(shí),創(chuàng)建具有給定許可數(shù)的計(jì)數(shù)信號(hào)量并設(shè)置為公平信號(hào)量,那么Semaphore會(huì)把之前等待的線程放到FIFO的隊(duì)列里,以便于當(dāng)有了新的許可證時(shí),可以分發(fā)給之前等了最長(zhǎng)時(shí)間的線程。
- void acquire():從此信號(hào)量獲取一個(gè)許可前線程將一直阻塞。相當(dāng)于一輛車占了一個(gè)車位。
- void acquire(int n):從此信號(hào)量獲取給定數(shù)目許可,在提供這些許可前一直將線程阻塞。比如n=2,就相當(dāng)于一輛車占了兩個(gè)車位。
- void acquireUninterruptibly():作用是使等待進(jìn)入acquire()方法的線程,不允許被中斷。acquireUninterruptibly()還有重載的方法acquireUninterruptibly(int permits),此方法的作用是在等待permits的情況下不允許被中斷,如果成功獲得鎖,則取得指定permits的個(gè)數(shù)。
- boolean tryAcquire():從信號(hào)量嘗試獲取一個(gè)許可,如果無(wú)可用許可,直接返回false,不會(huì)阻塞
- boolean tryAcquire(int permits): 嘗試獲取指定數(shù)目的許可,如果無(wú)可用許可直接返回false
- boolean tryAcquire(int permits, long timeout, TimeUnit unit): 在指定的時(shí)間內(nèi)嘗試從信號(hào)量中獲取許可,如果在指定的時(shí)間內(nèi)獲取成功,返回true,否則返回false
- void release():釋放一個(gè)許可,別忘了在finally中使用,注意:多次調(diào)用該方法,會(huì)使信號(hào)量的許可數(shù)增加,達(dá)到動(dòng)態(tài)擴(kuò)展的效果,如:初始permits為1, 調(diào)用了兩次release,最大許可會(huì)改變?yōu)?
- void release(int n):釋放n個(gè)許可。
- int availablePermits():當(dāng)前可用的許可數(shù)。
/**
* @Description: 演示Semaphore用法
*/
public class SemaphoreDemo {
public static Semaphore semaphore = new Semaphore(3,true);
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(50);
for (int i = 0; i < 100; i++) {
executorService.execute(new Task());
}
executorService.shutdown();
}
static class Task implements Runnable{
@Override
public void run() {
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+"拿到了許可證");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println(Thread.currentThread().getName()+"釋放了許可證");
semaphore.release();
}
}
}
}
Semaphore特殊用法
- 一次性獲取或釋放多個(gè)許可證
- 比如TaskA會(huì)調(diào)用很消耗資源的method1(),而TaskB調(diào)用的是不太消耗資源的method2(),假設(shè)我們一共有5個(gè)許可證,那么我們可以要求TaskA獲取5個(gè)許可證才能執(zhí)行,而TaskB只需要獲取到一個(gè)許可證就能執(zhí)行,這樣就避免了A和B同時(shí)運(yùn)行的情況,我們可以根據(jù)自己的需求合理分配資源
注意點(diǎn)
- 獲取和釋放的許可證數(shù)量必須一致,否則,比如每次都獲取2個(gè)但是只是放一個(gè)甚至不釋放,那么隨著時(shí)間的推移,到最后許可證不夠用,會(huì)導(dǎo)致程序卡死。(雖然信號(hào)量類并不對(duì)釋放和獲取的數(shù)量做規(guī)定,但是這是我們的編程規(guī)范,否則容易出錯(cuò))
- 注意在初始化Semaphore的時(shí)候設(shè)置公平性,一般設(shè)置為true更為合理
- 并不是必須由獲取許可證的線程來(lái)釋放許可證,事實(shí)上,獲取和釋放許可證對(duì)線程并無(wú)要求,也許是A獲取了,然后由B釋放,只要邏輯合理即可
- 信號(hào)量的作用,除了控制臨界區(qū)最多同時(shí)有N個(gè)線程訪問(wèn)外,另一個(gè)作用是可以實(shí)現(xiàn)“條件等待”,例如線程1需要在線程2完成準(zhǔn)備工作后才能開(kāi)始工作,那么線程1acquire(),而線程2完成任務(wù)后release(),這樣的話相當(dāng)于輕量級(jí)的CountDownLatch
Condition接口(又稱條件對(duì)象)
在使用Lock之前,我們使用的最多的同步方式應(yīng)該是synchronized關(guān)鍵字來(lái)實(shí)現(xiàn)同步方式了。配合Object的wait()、notify()系列方法可以實(shí)現(xiàn)等待/通知模式。Condition接口也提供了類似Object的監(jiān)視器方法,與Lock配合可以實(shí)現(xiàn)等待/通知模式,但是這兩者在使用方式以及功能特性上還是有差別的。Object和Condition接口的一些對(duì)比。摘自《Java并發(fā)編程的藝術(shù)》

首先我們需要明白condition對(duì)象是依賴于lock對(duì)象的,意思就是說(shuō)condition對(duì)象需要通過(guò)lock對(duì)象進(jìn)行創(chuàng)建出來(lái)(調(diào)用Lock對(duì)象的newCondition()方法)。condition的使用方式非常的簡(jiǎn)單。但是需要注意在調(diào)用方法前獲取鎖。
condition是對(duì)線程進(jìn)行控制管理的接口,具體實(shí)現(xiàn)是AQS的一個(gè)內(nèi)部類ConditionObject,主要功能是控制線程的啟/停(這么說(shuō)并不嚴(yán)格,還要有鎖的競(jìng)爭(zhēng)排隊(duì))。
Condition作用
- 當(dāng)線程1需要等待某個(gè)條件的時(shí)候,它就去執(zhí)行condition.await()方法,一旦執(zhí)行了await()方法,線程就進(jìn)入了阻塞狀態(tài)
-
然后通常會(huì)有另外一個(gè)線程,假設(shè)是線程2去執(zhí)行對(duì)應(yīng)的條件,直到這個(gè)條件達(dá)成的時(shí)候,線程2就會(huì)執(zhí)行condition.signal()方法,這時(shí)JVM就會(huì)從被阻塞的線程里找到那些等待該condition的線程,那么當(dāng)線程1收到可執(zhí)行信號(hào)的時(shí)候,它的線程狀態(tài)就會(huì)變成Runnable可執(zhí)行狀態(tài)
signalAll()和signal()區(qū)別
- signalAll()會(huì)喚起所有的等待線程
- 但signal()是公平的,只會(huì)喚起那個(gè)等待時(shí)間最長(zhǎng)的線程
Condition基本用法
/**
* @Description: 演示Condition的基本用法
*/
public class ConditionDemo1 {
public ReentrantLock lock = new ReentrantLock();
public Condition condition = lock.newCondition();
public void method1(){
lock.lock();
try{
System.out.println("條件不滿足,開(kāi)始await");
condition.await();
System.out.println("條件滿足,開(kāi)始執(zhí)行后續(xù)任務(wù)");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void method2(){
lock.lock();
try{
System.out.println("準(zhǔn)備工作完成,開(kāi)始喚醒其他線程");
condition.signal();
}finally {
lock.unlock();
}
}
public static void main(String[] args) {
ConditionDemo1 conditionDemo1 = new ConditionDemo1();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
conditionDemo1.method2();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
conditionDemo1.method1();
}
}
Condition實(shí)現(xiàn)生產(chǎn)者和消費(fèi)者模式
/**
* @Description: 演示Condition實(shí)現(xiàn)生產(chǎn)者和消費(fèi)者模式
*/
public class ConditionDemo2 {
private int queueSize = 10;
private PriorityQueue<Integer> queue = new PriorityQueue<>(queueSize);
private ReentrantLock lock = new ReentrantLock();
private Condition notFull = lock.newCondition();
private Condition notEmpty = lock.newCondition();
public static void main(String[] args) {
ConditionDemo2 conditionDemo2 = new ConditionDemo2();
Producer producer = conditionDemo2.new Producer();
Consumer consumer = conditionDemo2.new Consumer();
new Thread(producer).start();
new Thread(consumer).start();
}
class Consumer implements Runnable{
@Override
public void run() {
consume();
}
public void consume(){
while(true){
lock.lock();
try{
while (queue.size() == 0){
System.out.println("隊(duì)列空,等待數(shù)據(jù)");
notEmpty.await();
}
Integer poll = queue.poll();//走過(guò)await()證明隊(duì)列不為空,取出數(shù)據(jù)
System.out.println("消費(fèi)者消費(fèi)數(shù)據(jù):"+poll+",隊(duì)列剩余數(shù)據(jù)數(shù)量:"+queue.size());
notFull.signalAll();//獲取數(shù)據(jù)之后,隊(duì)列肯定有空閑,那么喚醒生產(chǎn)者進(jìn)行生產(chǎn)
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
class Producer implements Runnable{
@Override
public void run() {
produce();
}
public void produce(){
while(true){
lock.lock();
try{
while (queue.size() == queueSize){
System.out.println("隊(duì)列已滿,等待空余");
notFull.await();
}
queue.offer(1);//走過(guò)await()證明隊(duì)列有空閑,開(kāi)始往隊(duì)列里生產(chǎn)數(shù)據(jù)
System.out.println("生產(chǎn)者向隊(duì)列生產(chǎn)一個(gè)數(shù)據(jù),隊(duì)列剩余空間:"+(queueSize-queue.size()));
notEmpty.signalAll();//向隊(duì)列生產(chǎn)數(shù)據(jù)之后,隊(duì)列不為空,那么喚醒消費(fèi)者進(jìn)行消費(fèi)
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
}
Condition注意點(diǎn)
- 實(shí)際上,如果說(shuō)Lock用來(lái)替代synchronized,那么Condition就是用來(lái)代替相對(duì)應(yīng)的Object.wait/notify的,所以在用法和性質(zhì)上,幾乎一樣
- await方法會(huì)自動(dòng)釋放持有的Lock鎖,和Object.wait一樣,不需要自己手動(dòng)先釋放鎖,而sleep()并不釋放鎖。
- 調(diào)用await的時(shí)候,必須持有鎖,否則會(huì)拋異常,和Object.wait一樣
CyclicBarrier循環(huán)柵欄
CyclicBarrier是java.util.concurrent包下面的一個(gè)工具類,字面意思是可循環(huán)使用(Cyclic)的屏障(Barrier),通過(guò)它可以實(shí)現(xiàn)讓一組線程到達(dá)一個(gè)屏障(也可以叫同步點(diǎn))時(shí)被阻塞,直到最后一個(gè)線程到達(dá)屏障時(shí),所有被屏障攔截的線程才會(huì)繼續(xù)執(zhí)行。
CyclicBarrier循環(huán)柵欄和CountDownLatch很類似,都能阻塞一組線程
柵欄類似于閉鎖,它能阻塞一組線程直到某個(gè)事件的發(fā)生。柵欄與閉鎖的關(guān)鍵區(qū)別在于,所有的線程必須同時(shí)到達(dá)柵欄位置,才能繼續(xù)執(zhí)行。閉鎖用于等待事件,而柵欄用于等待其他線程。
CyclicBarrier可以使一定數(shù)量的線程反復(fù)地在柵欄位置處匯集。當(dāng)線程到達(dá)柵欄位置時(shí)將調(diào)用await方法,這個(gè)方法將阻塞直到所有線程都到達(dá)柵欄位置。如果所有線程都到達(dá)柵欄位置,那么柵欄將打開(kāi),此時(shí)所有的線程都將被釋放,而柵欄將被重置以便下次使用。
CyclicBarrier支持一個(gè)可選的Runnable命令,每個(gè)屏障點(diǎn)運(yùn)行一次,在派對(duì)中的最后一個(gè)線程到達(dá)之后,但在任何線程釋放之前。 在任何一方繼續(xù)進(jìn)行之前,此屏障操作對(duì)更新共享狀態(tài)很有用。
實(shí)現(xiàn)原理:在CyclicBarrier的內(nèi)部定義了一個(gè)Lock對(duì)象,每當(dāng)一個(gè)線程調(diào)用await方法時(shí),將攔截的線程數(shù)減1,然后判斷剩余攔截?cái)?shù)是否為初始值parties,如果不是,進(jìn)入Lock對(duì)象的條件隊(duì)列等待。如果是,執(zhí)行barrierAction對(duì)象的Runnable方法,然后將鎖的條件隊(duì)列中的所有線程放入鎖等待隊(duì)列中,這些線程會(huì)依次的獲取鎖、釋放鎖。
CyclicBarrier構(gòu)造方法
- CyclicBarrier(int parties)
創(chuàng)建一個(gè)新的 CyclicBarrier ,當(dāng)給定數(shù)量的線程(線程)等待它時(shí),它將跳閘,并且當(dāng)屏障跳閘時(shí)不執(zhí)行預(yù)定義的動(dòng)作。 - CyclicBarrier(int parties, Runnable barrierAction)
創(chuàng)建一個(gè)新的 CyclicBarrier ,當(dāng)給定數(shù)量的線程(線程)等待時(shí),它將跳閘,當(dāng)屏障跳閘時(shí)執(zhí)行給定的屏障動(dòng)作,由最后一個(gè)進(jìn)入屏障的線程執(zhí)行。
CyclicBarrier方法
- int await() 等待所有 parties已經(jīng)在這個(gè)障礙上調(diào)用了 await 。
- int await(long timeout, TimeUnit unit) 等待所有 parties已經(jīng)在此屏障上調(diào)用 await ,或指定的等待時(shí)間過(guò)去。
- int getNumberWaiting() 返回目前正在等待障礙的各方的數(shù)量。
- int getParties() 返回旅行這個(gè)障礙所需的parties數(shù)量。
- boolean isBroken() 查詢這個(gè)障礙是否處于破碎狀態(tài)。
- void reset() 將屏障重置為初始狀態(tài)。
CyclicBarrier的用法
/**
* @Description: 演示CyclicBarrier的使用
*/
public class CyclicBarrierDemo {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(5, new Runnable() {
@Override
public void run() {
System.out.println("所有人都到場(chǎng)了,大家統(tǒng)一出發(fā)");
}
});
for (int i = 0; i < 10; i++) {
new Thread(new Task(i,cyclicBarrier)).start();
}
}
static class Task implements Runnable{
private int id;
private CyclicBarrier cyclicBarrier;
public Task(int id, CyclicBarrier cyclicBarrier) {
this.id = id;
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+",id:"+id+"現(xiàn)前往集合地點(diǎn)");
try {
Thread.sleep(new Random().nextInt(10000));
System.out.println(Thread.currentThread().getName()+"到了集合地點(diǎn),開(kāi)始等待其他人到達(dá)");
cyclicBarrier.await();
System.out.println(Thread.currentThread().getName()+"出發(fā)了");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
}
CyclicBarrier和CountDownLatch的區(qū)別
- 作用不同:CyclicBarrier要等固定數(shù)量的線程都到達(dá)了柵欄位置才能繼續(xù)執(zhí)行,而CountDownLatch只需要等待數(shù)字到0,也就是說(shuō),CountDownLatch用于事件,CyclicBarrier是用于線程的
- 可重用性不同:CountDownLatch在倒數(shù)到0并觸發(fā)門(mén)閂打開(kāi)后,就不能再次使用了,除非新建實(shí)例;而CyclicBarrier可以重復(fù)使用
- CountDownLatch是減計(jì)數(shù)方式,而CyclicBarrier是加計(jì)數(shù)方式。
- CountDownLatch計(jì)數(shù)為0無(wú)法重置,而CyclicBarrier計(jì)數(shù)達(dá)到初始值,則可以重置。
參考:
https://www.cnblogs.com/Lee_xy_z/p/10470181.html
https://www.cnblogs.com/wxgblogs/p/5422508.html
