CountDownLatch是一个同步的辅助类,允许一个或多个线程,等待其他一组线程完成操作,再继续执行。
CyclicBarrier是一个同步的辅助类,允许一组线程之间相互等待,达到一个共同点,再继续执行。
CountDownLatch的计数器无法被重置;CyclicBarrier的计数器可以被重置后使用,因此它被称为是循环的barrier。
他们都是:Synchronization aid,把它翻译成同步辅助器,既然是辅助工具,怎么使用?哪些场景使用?
个人理解:把CountDownLatch理解成倒计时锁。
场景还原:一年级期末考试要开始了,监考老师发下去试卷,然后坐在讲台旁边玩着手机等待着学生答题,有的学生提前交了试卷,并约起打球了,等到最后一个学生交卷了,老师开始整理试卷,贴封条,下班。
补充场景:我们在玩LOL英雄联盟时会出现十个人不同加载状态,但是最后一个人由于各种原因始终加载不了100%,于是游戏系统自动等待所有玩家的状态都准备好,才展现游戏画面。
个人理解:把CyclicBarrier看成是个障碍,所有的线程必须到齐后才能一起通过这个障碍
场景还原:在军队里,士兵经常训练的项目有翻越高墙、双杠练习、四百米障碍、越野三公里跑等。将士兵分成多队进行比赛,每队的成员都必须共同完成一个关卡之后,才能一起前往下一个关卡,也就是说,同一个队伍中,先完成这一关的士兵,必须等待同队中其他士兵都完成了,才能一起进入到下一个比赛关卡。
1、CountDownLatch的原理和使用
CountDownLatch是通过“共享锁”实现的。
CountDownLatch的方法说明:
public CountDownLatch(int count)构造函数的作用是创建一个指定计数的锁存器。
public void countDown()方法的作用是递减锁存器的计数,如果计数为零,则释放所有等待的线程。如果当前计数大于零,则让计数递减。线程每调用一次该方法,计数就会减一。若当前计数已经等于0,则调用该方法不会发生任何事情。
public void await() throws InterruptedException方法的作用是使当前线程一直等待,直到锁存器计数为零,或者线程被中断。如果当前计数为零,则此方法立即返回。
调用await()方法的线程将一直处于等待状态,除非发生以下两种情况之一:
1.其他线程调用countDown()方法让锁存器的计数递减为0;
2.其他线程中断当前线程。
public boolean await(long timeout, TimeUnit unit) throws InterruptedException方法的作用是使当前线程一直等待,直到锁存器计数为零,或者线程被中断,或者已超出指定的等待时间。
调用await(timeout, unit)方法的线程将一直处于等待状态,除非发生以下三种情况之一:
1.其他线程调用countDown()方法让锁存器的计数递减为0;
2.其他线程中断当前线程;
3.超出指定的等待时间。
CountDownLatch的典型用法:
1、某一线程在开始运行前等待n个线程执行完毕。
初始化计数为n的CountDownLatch latch = new CountDownLatch(n)对象,每当一个线程执行完毕,就在当前线程中调用latch.countDown()方法,让计数器减1,当计数器的值变为0时,调用latch.await()方法的线程就会被唤醒。
一个典型应用场景就是启动一个服务时,主线程需要等待多个组件加载完毕,之后再继续执行。
2、实现多个线程开始执行任务的最大并行性。
注意是并行性,不是并发,强调的是多个线程在某一时刻同时开始执行。类似于赛跑,多个运动员在起点,等待发令枪响,然后同时开跑。做法是初始化一个计数为1的CountDownLatch latch = new CountDownLatch(1)对象,多个线程在开始执行任务前,首先调用latch.await()方法进行等待,当主线程调用latch.countDown()方法时,计数器变为0,多个线程同时被唤醒。
示例1:
package com.sort.study.thread;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CountDownLatchTest {
public static void main(String[] args) {
CountDownLatch latch = new CountDownLatch(3);
testCDL(latch);
System.out.println("主线程等待子线程任务执行结束......");
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("======主线程结束======");
}
private static void testCDL(CountDownLatch latch){
ExecutorService service = Executors.newFixedThreadPool(1);
Runnable runnable = new Runnable() {
@Override
public void run() {
// %n 表示换行
System.out.printf("当前线程[%s]开始执行任务......%n",
Thread.currentThread().getName());
waitTime(2000);
System.out.printf("当前线程[%s]执行任务结束。%n",
Thread.currentThread().getName());
//可以在一个线程中调用多次countDown方法,调用的次数要大于等于计数器的个数,否则主线程将一直等待
latch.countDown();
latch.countDown();
latch.countDown();
latch.countDown();
}
};
service.execute(runnable);
}
private static void waitTime(long time){
try {
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
输出结果:
主线程等待子线程任务执行结束......
当前线程[pool-1-thread-1]开始执行任务......
当前线程[pool-1-thread-1]执行任务结束。
====主线程结束====
示例2:
package com.sort.study.thread;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CountDownLatchTest {
public static void main(String[] args) {
int count = 5;
CountDownLatch latch = new CountDownLatch(count);
testCDL(latch,count);
System.out.println("监考老师等待所有学生答题结束......");
try {
//在主线程中调用CountDownLatch对象的await方法,让主线程等待
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("====所有学生答题结束,监考老师封装试卷====");
}
private static void testCDL(CountDownLatch latch, int count){
ExecutorService service = Executors.newFixedThreadPool(count);
for (int i=0;i<count;i++){
//创建count个线程
Runnable runnable = new Runnable() {
@Override
public void run() {
// %n 表示换行
System.out.printf("学生[%s]开始考试答题......%n",
Thread.currentThread().getName());
waitTime(2000);
System.out.printf("学生[%s]答题结束,提交试卷。%n",
Thread.currentThread().getName());
//在线程中调用CountDownLatch对象的countDown方法,让计数器减一
latch.countDown();
}
};
service.execute(runnable);
}
}
private static void waitTime(long time){
try {
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
输出结果(下面是可能的一种输出结果):
学生[pool-1-thread-1]开始考试答题......
学生[pool-1-thread-4]开始考试答题......
学生[pool-1-thread-5]开始考试答题......
监考老师等待所有学生答题结束......
学生[pool-1-thread-3]开始考试答题......
学生[pool-1-thread-2]开始考试答题......
学生[pool-1-thread-1]答题结束,提交试卷。
学生[pool-1-thread-3]答题结束,提交试卷。
学生[pool-1-thread-4]答题结束,提交试卷。
学生[pool-1-thread-5]答题结束,提交试卷。
学生[pool-1-thread-2]答题结束,提交试卷。
====所有学生答题结束,监考老师封装试卷====
示例3:
package com.sort.study.thread;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CountDownLatchTest {
public static void main(String[] args) {
CountDownLatchTest cdl = new CountDownLatchTest();
cdl.runRace();
}
private void runRace(){
int count = 3;
//初始化计数器为1的CountDownLatch,表示裁判员发枪的latch
CountDownLatch refereeLatch = new CountDownLatch(1);
//初始化计数器为count的CountDownLatch,表示运动员的latch
CountDownLatch athleteLatch = new CountDownLatch(count);
//创建保存运动员信息的容器
List<Runner> list = Collections.synchronizedList(new ArrayList<>());
//让运动员等待裁判员发枪,准备开跑
this.athleteRun(refereeLatch,athleteLatch,count,list);
this.waitTime(1000);
//让裁判员发枪,并等待统计成绩
this.refereeTiming(refereeLatch,athleteLatch,list);
}
private void athleteRun(CountDownLatch refereeLatch, CountDownLatch athleteLatch, int num, List<Runner> list){
ExecutorService service = Executors.newCachedThreadPool();
for (int i=0;i<num;i++){
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.printf("运动员[%s]等待裁判发枪开跑。%n",
Thread.currentThread().getName());
Runner runner = new Runner(Thread.currentThread().getName());
try {
//当前线程调用CountDownLatch对象的await方法进行等待
//等待裁判员发枪之后,运动员才开跑
refereeLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("运动员[%s]开始跑步......%n",
Thread.currentThread().getName());
Random random = new Random();
int time = random.nextInt(5) * 1000;
waitTime(time);
runner.setTime(time);
list.add(runner);
System.out.printf("运动员[%s]跑到终点。%n",
Thread.currentThread().getName());
//当前线程调用CountDownLatch对象的countDown方法让计数器减一
//每个运动员跑完之后都将计数器减一,将等待统计成绩的裁判唤醒
athleteLatch.countDown();
}
};
//执行任务
service.execute(runnable);
}
//关闭线程池
service.shutdown();
}
private void refereeTiming(CountDownLatch refereeLatch, CountDownLatch athleteLatch, List<Runner> list){
System.out.printf("裁判员[%s]开始准备发枪......%n",
Thread.currentThread().getName());
//调用countDown方法让计数器减一,裁判发枪让运动员开跑
refereeLatch.countDown();
System.out.printf("裁判员[%s]等待统计成绩......%n",
Thread.currentThread().getName());
try {
//等待所有的运动员都跑完之后,裁判员再一起统计成绩
athleteLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("===所有运动员已跑完,裁判员[%s]统计成绩结束===%n",
Thread.currentThread().getName());
list.forEach(r -> {
System.out.println(String.format("====运动员[%s]跑步花费的时间是[%d]====",r.getName(),r.getTime()));
});
}
private void waitTime(long time){
long l = (long) (Math.random() * time);
try {
Thread.sleep(l);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private static class Runner{
private String name;
private long time;
public Runner() {
}
public Runner(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
}
}
输出结果:
运动员[pool-1-thread-1]等待裁判发枪开跑。
运动员[pool-1-thread-3]等待裁判发枪开跑。
运动员[pool-1-thread-2]等待裁判发枪开跑。
裁判员[main]开始准备发枪......
裁判员[main]等待统计成绩......
运动员[pool-1-thread-2]开始跑步......
运动员[pool-1-thread-1]开始跑步......
运动员[pool-1-thread-3]开始跑步......
运动员[pool-1-thread-3]跑到终点。
运动员[pool-1-thread-1]跑到终点。
运动员[pool-1-thread-2]跑到终点。
===所有运动员已跑完,裁判员[main]统计成绩结束===
====运动员[pool-1-thread-3]跑步花费的时间是[1000]====
====运动员[pool-1-thread-1]跑步花费的时间是[2000]====
====运动员[pool-1-thread-2]跑步花费的时间是[4000]====
2、CyclicBarrier的原理和使用
现实生活中我们经常会遇到这样的情景,在进行某个活动前需要等待人全部都齐了才开始。例如吃饭时要等全家人都上座了才动筷子,旅游时要等全部人都到齐了才出发,比赛时要等运动员都上场后才开始。
在JUC包中为我们提供了一个同步工具类能够很好的模拟这类场景,它就是CyclicBarrier类。利用CyclicBarrier类可以实现一组线程相互等待,当所有线程都到达某个屏障点后再进行后续的操作。
CyclicBarrier字面意思是“可重复使用的栅栏”,CyclicBarrier 相比 CountDownLatch 来说,要简单很多,其源码没有什么高深的地方,它是 ReentrantLock 和 Condition 的组合使用。
如下示意图,CyclicBarrier和CountDownLatch是不是很像,只是CyclicBarrier可以有不止一个栅栏,因为它的栅栏(Barrier)可以重复使用(Cyclic)。
首先,CyclicBarrier 的源码实现和 CountDownLatch 大同小异,CountDownLatch 基于 AQS 的共享模式的使用,而 CyclicBarrier 基于 Condition 来实现的。
在CyclicBarrier类的内部有一个计数器,每个线程在到达屏障点的时候都会调用await方法将自己阻塞,此时计数器会减1,当计数器减为0的时候所有因调用await方法而被阻塞的线程将被唤醒。这就是实现一组线程相互等待的原理。下面看一下CyclicBarrier的源码,由源码可知,在CyclicBarrier内部维护了ReentrantLock和Condition,所以CyclicBarrier是基于ReentrantLock + Condition实现的。
public class CyclicBarrier {
/**内部类Generation是用来处理循环使用的问题,维护了一个broker状态表示当前的栅栏是否失效。如果失效,可以重置栅栏的状态。当栅栏被打破时,就设置当前generation的broker为true表示失效,并唤醒所有等待的线程*/
private static class Generation {
boolean broken = false;
}
/** 用于守卫屏障入口的同步锁 */
private final ReentrantLock lock = new ReentrantLock();
/** 创建一个条件队列,让所有线程在同一个条件队列上等待,当最后一个线程到达时,调用signalAll方法唤醒所有等待的线程*/
private final Condition trip = lock.newCondition();
/** 每次拦截的线程数 */
private final int parties;
/**换代前执行的任务,当所有线程都到达屏障时,由最后一个到达的线程执行该任务*/
private final Runnable barrierCommand;
/**栅栏的当前代*/
private Generation generation = new Generation();
/**初始值与parties相同,随着每次await方法的调用而减1,直到减为0就将所有线程唤醒。在每一个新代或屏障被打破时,count都会被重置为parties的值。*/
private int count;
/**唤醒所有线程并开启下一个屏障代,仅在持有锁时被调用。*/
private void nextGeneration() {
// signal completion of last generation
trip.signalAll();
// set up next generation
count = parties;
generation = new Generation();
}
/**将当前屏障代的状态设置为已打破并唤醒所有线程,仅在持有锁时被调用。*/
private void breakBarrier() {
generation.broken = true;
count = parties;
trip.signalAll();
}
/**
* 主要的屏障代码,包含各种策略。
*/
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
final ReentrantLock lock = this.lock;
lock.lock();
try {
final Generation g = generation;
if (g.broken)
throw new BrokenBarrierException();
//如果当前线程被中断,则打破屏障,唤醒所有线程,并抛出已中断异常
if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
}
int index = --count;
//如果屏障被绊倒(tripped),即被打破,则说明当前线程是最后一个到达屏障的线程
if (index == 0) { // tripped
boolean ranAction = false;
try {
final Runnable command = barrierCommand;
//在创建CyclicBarrier时,如果指定的Runnable不为空,则由当前线程(也是最后一个线程)执行该Runnable
if (command != null)
command.run();
ranAction = true;
//在该方法中唤醒所有线程,并创建新的屏障代,也就是说当前屏障被打破时,会创建一个新的屏障
nextGeneration();
return 0;
} finally {
if (!ranAction)
breakBarrier();
}
}
// loop until tripped, broken, interrupted, or timed out
for (;;) {
try {
if (!timed)
trip.await();
else if (nanos > 0L)
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) {
if (g == generation && ! g.broken) {
breakBarrier();
throw ie;
} else {
// We're about to finish waiting even if we had not
// been interrupted, so this interrupt is deemed to
// "belong" to subsequent execution.
Thread.currentThread().interrupt();
}
}
if (g.broken)
throw new BrokenBarrierException();
if (g != generation)
return index;
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
lock.unlock();
}
}
/**
* Creates a new {@code CyclicBarrier} that will trip when the
* given number of parties (threads) are waiting upon it, and which
* will execute the given barrier action when the barrier is tripped,
* performed by the last thread entering the barrier.
创建一个新的CyclicBarrier循环屏障,当给定数量的线程都到达该屏障前等待时,该屏障将会被绊倒(trip),也就是该屏障会被打破,所有线程都被唤醒,越过屏障。当屏障被打破时,由最后一个进入屏障的线程执行指定的屏障操作barrierAction。
*
* @param parties the number of threads that must invoke {@link #await}
* before the barrier is tripped
* @param barrierAction the command to execute when the barrier is
* tripped, or {@code null} if there is no action
* @throws IllegalArgumentException if {@code parties} is less than 1
*/
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties;
this.barrierCommand = barrierAction;
}
/**
* Creates a new {@code CyclicBarrier} that will trip when the
* given number of parties (threads) are waiting upon it, and
* does not perform a predefined action when the barrier is tripped.
*
* @param parties the number of threads that must invoke {@link #await}
* before the barrier is tripped
* @throws IllegalArgumentException if {@code parties} is less than 1
*/
public CyclicBarrier(int parties) {
this(parties, null);
}
/**
* Returns the number of parties required to trip this barrier.
*
* @return the number of parties required to trip this barrier
*/
public int getParties() {
return parties;
}
/**一直等待,直到所有线程调用在这个屏障上的await方法
* Waits until all {@linkplain #getParties parties} have invoked
* {@code await} on this barrier.
*
如果当前线程不是最后一个到达的线程,则出于线程调度目的,它将被禁用,并处于休眠状态(dormant),直到发生以下情况之一:
最后一个线程到达;或
其他线程中断当前线程;或
其他线程中断另一个等待的线程;或
其他线程在等待屏障时超时;或
其他一些线程在此屏障上调用重置。
* <p>If the current thread is not the last to arrive then it is
* disabled for thread scheduling purposes and lies dormant until
* one of the following things happens:
* <ul>
* <li>The last thread arrives; or
* <li>Some other thread {@linkplain Thread#interrupt interrupts}
* the current thread; or
* <li>Some other thread {@linkplain Thread#interrupt interrupts}
* one of the other waiting threads; or
* <li>Some other thread times out while waiting for barrier; or
* <li>Some other thread invokes {@link #reset} on this barrier.
* </ul>
*/
public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}
public int await(long timeout, TimeUnit unit)
throws InterruptedException,
BrokenBarrierException,
TimeoutException {
return dowait(true, unit.toNanos(timeout));
}
/**
* Queries if this barrier is in a broken state.
*
* @return {@code true} if one or more parties broke out of this
* barrier due to interruption or timeout since
* construction or the last reset, or a barrier action
* failed due to an exception; {@code false} otherwise.
*/
public boolean isBroken() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return generation.broken;
} finally {
lock.unlock();
}
}
/**
* Resets the barrier to its initial state. If any parties are
* currently waiting at the barrier, they will return with a
* {@link BrokenBarrierException}. Note that resets <em>after</em>
* a breakage has occurred for other reasons can be complicated to
* carry out; threads need to re-synchronize in some other way,
* and choose one to perform the reset. It may be preferable to
* instead create a new barrier for subsequent use.
*/
public void reset() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
breakBarrier(); // break the current generation
nextGeneration(); // start a new generation
} finally {
lock.unlock();
}
}
/**
* Returns the number of parties currently waiting at the barrier.
* This method is primarily useful for debugging and assertions.
*
* @return the number of parties currently blocked in {@link #await}
*/
public int getNumberWaiting() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return parties - count;
} finally {
lock.unlock();
}
}
}
示例1:运动员跑步比赛时需要等待大家都准备好了才能一起开跑。
package com.sort.study.thread;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CyclicBarrierTest {
public static void main(String[] args) {
int count = 3;
CyclicBarrier barrier = new CyclicBarrier(count);
CyclicBarrierTest cbt = new CyclicBarrierTest();
cbt.runRace(count,barrier);
System.out.println("=====主线程结束=====");
}
private void runRace(int count, CyclicBarrier barrier){
ExecutorService service = Executors.newFixedThreadPool(count);
for (int i=0;i<count;i++){
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.printf("当前运动员[%s]已准备好,等待其他运动员准备......%n",Thread.currentThread().getName());
try {
/*除了最后一个线程调用await方法时不会被阻塞,其他线程调用await方法时都会被阻塞,而且最后一个线程调用await方法时,底层会在nextGeneration方法中调用signalAll方法唤醒其他所有线程*/
barrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
System.out.printf("所有运动员都已准备好,当前运动员[%s]开始起跑。%n",Thread.currentThread().getName());
waitTime(5000);
System.out.printf("当前运动员[%s]已跑完。%n", Thread.currentThread().getName());
}
};
service.execute(runnable);
}
//关闭线程池
service.shutdown();
}
private void waitTime(long time){
long l = (long) (Math.random() * time);
try {
Thread.sleep(l);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
输出结果:
=====主线程结束=====
当前运动员[pool-1-thread-1]已准备好,等待其他运动员准备......
当前运动员[pool-1-thread-2]已准备好,等待其他运动员准备......
当前运动员[pool-1-thread-3]已准备好,等待其他运动员准备......
所有运动员都已准备好,当前运动员[pool-1-thread-3]开始起跑。
所有运动员都已准备好,当前运动员[pool-1-thread-1]开始起跑。
所有运动员都已准备好,当前运动员[pool-1-thread-2]开始起跑。
当前运动员[pool-1-thread-2]已跑完。
当前运动员[pool-1-thread-3]已跑完。
当前运动员[pool-1-thread-1]已跑完。
示例2:
package com.sort.study.thread;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 需求:四个队员需要通过三关才能闯关成功,在闯每一关时,每个人可以单独做自己的闯关任务,在到达每一关的关卡时必须等待所有队员一起才能到下一关
*/
public class CyclicBarrierTest2 {
private static final List<BreakGateRunnable> LIST = new ArrayList<>();
public static void main(String[] args) {
CyclicBarrierTest2 cbt = new CyclicBarrierTest2();
cbt.breakGate();
System.out.println("====主线程结束====");
}
private void breakGate(){
int count = 4;
Runnable runnable = new BreakGateInfoRunnable();
//最后一个线程在调用CyclicBarrier对象的await方法时,会调用此处runnable对象的run方法
CyclicBarrier barrier = new CyclicBarrier(count, runnable);
ExecutorService service = Executors.newFixedThreadPool(count);
for (int i=0;i<count;i++){
BreakGateRunnable gateRun = new BreakGateRunnable(barrier);
LIST.add(gateRun);
service.execute(gateRun);
}
service.shutdown();
}
static class BreakGateInfoRunnable implements Runnable{
@Override
public void run() {
for (BreakGateRunnable run : CyclicBarrierTest2.LIST){
System.out.println(run.getBreakGateInfo());
}
System.out.println("所有队员都已到达,准备进入下一关。");
System.out.println("======================================");
}
}
}
class BreakGateRunnable implements Runnable{
private String breakGateInfo;
private final CyclicBarrier barrier;
public BreakGateRunnable(CyclicBarrier barrier){
this.barrier = barrier;
}
@Override
public void run() {
int i = 0;
int gateNum = 3;
while (i < gateNum){
//模拟闯关耗时
long time = (long) (Math.random() * 3000);
waitTime(time);
breakGateInfo = String.format("队员[%s]闯第[%d]关耗时[%d]。",
Thread.currentThread().getName(),(i+1),time);
System.out.printf("当前队员[%s]闯完第[%d]关,等待其他队员一起过关。%n",
Thread.currentThread().getName(),(i+1));
try {
//等待所有队员一起过关
barrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
i++;
}
}
//获取闯关信息
public String getBreakGateInfo(){
return breakGateInfo;
}
private void waitTime(long time){
try {
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
输出结果:
====主线程结束====
当前队员[pool-1-thread-1]闯完第[1]关,等待其他队员一起过关。
当前队员[pool-1-thread-2]闯完第[1]关,等待其他队员一起过关。
当前队员[pool-1-thread-3]闯完第[1]关,等待其他队员一起过关。
当前队员[pool-1-thread-4]闯完第[1]关,等待其他队员一起过关。
队员[pool-1-thread-1]闯第[1]关耗时[442]。
队员[pool-1-thread-2]闯第[1]关耗时[1036]。
队员[pool-1-thread-3]闯第[1]关耗时[1468]。
队员[pool-1-thread-4]闯第[1]关耗时[2011]。
所有队员都已到达,准备进入下一关。
======================================
当前队员[pool-1-thread-3]闯完第[2]关,等待其他队员一起过关。
当前队员[pool-1-thread-1]闯完第[2]关,等待其他队员一起过关。
当前队员[pool-1-thread-4]闯完第[2]关,等待其他队员一起过关。
当前队员[pool-1-thread-2]闯完第[2]关,等待其他队员一起过关。
队员[pool-1-thread-1]闯第[2]关耗时[829]。
队员[pool-1-thread-2]闯第[2]关耗时[1820]。
队员[pool-1-thread-3]闯第[2]关耗时[7]。
队员[pool-1-thread-4]闯第[2]关耗时[1742]。
所有队员都已到达,准备进入下一关。
======================================
当前队员[pool-1-thread-3]闯完第[3]关,等待其他队员一起过关。
当前队员[pool-1-thread-1]闯完第[3]关,等待其他队员一起过关。
当前队员[pool-1-thread-2]闯完第[3]关,等待其他队员一起过关。
当前队员[pool-1-thread-4]闯完第[3]关,等待其他队员一起过关。
队员[pool-1-thread-1]闯第[3]关耗时[600]。
队员[pool-1-thread-2]闯第[3]关耗时[1667]。
队员[pool-1-thread-3]闯第[3]关耗时[57]。
队员[pool-1-thread-4]闯第[3]关耗时[2372]。
所有队员都已到达,准备进入下一关。
======================================