CyclicBarrier
用来控制多个线程互相等待,只有当多个线程都到达时,这些线程才会继续执行。
和 CountdownLatch 相似,都是通过维护计数器来实现的。线程执行 await() 方法之后计数器会减 1,并进行等待,直到计数器为 0,所有调用 await() 方法而在等待的线程才能继续执行。
CyclicBarrier 和 CountdownLatch 的一个区别是,CyclicBarrier 的计数器通过调用 reset() 方法可以循环使用,所以它才叫做循环屏障。
CyclicBarrier 有两个构造函数,其中 parties 指示计数器的初始值,barrierAction 在所有线程都到达屏障的时候会执行一次。
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties;
this.barrierCommand = barrierAction;
}
public CyclicBarrier(int parties) {
this(parties, null);
}
demo示例
package com.leo.demo.juctest;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @ClassName: CyclicBarrierExample
* @Description: 关于CyclicBarrier的测试
* @Author: leo825
* @Date: 2020-04-28 23:48
* @Version: 1.0
*/
public class CyclicBarrierExample {
public static void main(String[] args) {
final int totalThread = 10;
CyclicBarrier cyclicBarrier = new CyclicBarrier(totalThread);
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < totalThread; i++) {
executorService.execute(() -> {
String threadName = Thread.currentThread().getName();
System.out.println(threadName + " 开始准备...");
try {
cyclicBarrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println(threadName + " 准备结束...");
});
}
executorService.shutdown();
}
}
执行结果
pool-1-thread-1 开始准备...
pool-1-thread-2 开始准备...
pool-1-thread-3 开始准备...
pool-1-thread-4 开始准备...
pool-1-thread-5 开始准备...
pool-1-thread-6 开始准备...
pool-1-thread-7 开始准备...
pool-1-thread-8 开始准备...
pool-1-thread-9 开始准备...
pool-1-thread-10 开始准备...
pool-1-thread-10 准备结束...
pool-1-thread-1 准备结束...
pool-1-thread-2 准备结束...
pool-1-thread-3 准备结束...
pool-1-thread-4 准备结束...
pool-1-thread-5 准备结束...
pool-1-thread-6 准备结束...
pool-1-thread-7 准备结束...
pool-1-thread-9 准备结束...
pool-1-thread-8 准备结束...
CyclicBarrier 和 CountdownLatch
CyclicBarrier 和 CountdownLatch结合使用
有一个场景:男子100米短跑,需要知道每一个运动员成绩,并且打印第一名和最后一名。
具体实现如下:
package com.leo.demo.threadtest.countdownlatch;
import java.math.BigDecimal;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @ClassName: Runner
* @Description: 设计百米赛跑统计时间的小程序
* 1、10个选手听信号枪,统一开始跑步
* 2、打印出来每个选手耗时时间单位秒
* 3、打印比赛总共耗时时间单位秒
* @Author: leo825
* @Date: 2019-08-21 09:28
* @Version: 1.0
*/
public class RunnerTest {
public static void main(String[] args) throws InterruptedException, BrokenBarrierException {
//设置10名跑步选手
int runners = 10;
//设置10+1个屏障,因为裁判也需要阻塞,裁判要先到终点
CyclicBarrier cyclicBarrier = new CyclicBarrier(runners + 1);
//设置主线程准备时间
CountDownLatch countDownLatch = new CountDownLatch(1);
//比赛准备时间
long beginTime = System.currentTimeMillis();
System.out.println("男子100米运动员就绪准备比赛");
//使用随机数产生随机运行时间
ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();
//使用原子变量,模拟第一名成绩
final AtomicInteger firstRunner = new AtomicInteger(0);
//使用原子变量,模拟最后一名成绩
final AtomicInteger lastRunner = new AtomicInteger(0);
//模拟赛道上的运动员
ExecutorService executor = Executors.newFixedThreadPool(runners);
//运动员进入各自的跑道
for (int i = 0; i < runners; i++) {
//模拟百米赛跑运动员耗时
int costTime = threadLocalRandom.nextInt(9690, 14000);
//模拟运动员再各自跑道准备跑步
executor.submit(() -> {
try {
String threadName = Thread.currentThread().getName();
System.out.println(threadName + "运动员就绪!!!");
try {
//等待主线程完成的信号
countDownLatch.await();
if (firstRunner.get() == 0 || lastRunner.get() == 0) {
firstRunner.set(costTime);
lastRunner.set(costTime);
} else {
if (firstRunner.get() > costTime) {
firstRunner.set(costTime);
}
if (lastRunner.get() < costTime) {
lastRunner.set(costTime);
}
}
TimeUnit.MICROSECONDS.sleep(costTime);
System.out.println("运动员" + threadName + " 男子100米成绩为:" + transtCostTime(costTime) + "s");
cyclicBarrier.await();
} catch (Exception e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
});
}
//主线程准备时间
long endTime = System.currentTimeMillis();
System.out.println(Thread.currentThread().getName() + "准备时间:" + (endTime - beginTime) + "ms");
//裁判发号施令开始比赛
System.out.println("开始比赛...");
countDownLatch.countDown();
//裁判事先到终点等待,当数字到达10+1的时候开始释放锁
cyclicBarrier.await();
System.out.println("百米赛跑第一名成绩:" + transtCostTime(firstRunner.get()) + "s");
System.out.println("百米赛跑最后一名成绩:" + transtCostTime(lastRunner.get()) + "s");
executor.shutdown();
}
/**
* 将毫秒转换成秒
*
* @param costTime
* @return
*/
private static BigDecimal transtCostTime(int costTime) {
BigDecimal bigDecimal = new BigDecimal(costTime);
bigDecimal = bigDecimal.divide(new BigDecimal(1000), 3, BigDecimal.ROUND_DOWN);
return bigDecimal;
}
}
打印结果如下:
男子100米运动员就绪准备比赛
main准备时间:180ms
开始比赛...
pool-1-thread-1运动员就绪!!!
pool-1-thread-2运动员就绪!!!
pool-1-thread-3运动员就绪!!!
pool-1-thread-6运动员就绪!!!
pool-1-thread-5运动员就绪!!!
pool-1-thread-4运动员就绪!!!
pool-1-thread-7运动员就绪!!!
pool-1-thread-8运动员就绪!!!
pool-1-thread-9运动员就绪!!!
pool-1-thread-10运动员就绪!!!
运动员pool-1-thread-4 男子100米成绩为:9.690s
运动员pool-1-thread-5 男子100米成绩为:10.209s
运动员pool-1-thread-2 男子100米成绩为:13.326s
运动员pool-1-thread-7 男子100米成绩为:9.692s
运动员pool-1-thread-10 男子100米成绩为:10.126s
运动员pool-1-thread-3 男子100米成绩为:10.350s
运动员pool-1-thread-8 男子100米成绩为:10.623s
运动员pool-1-thread-1 男子100米成绩为:11.263s
运动员pool-1-thread-6 男子100米成绩为:11.872s
运动员pool-1-thread-9 男子100米成绩为:11.433s
百米赛跑第一名成绩:9.690s
百米赛跑最后一名成绩:13.326s