线程-线程工具-CyclicBarrier
文章目录
前言
前面介绍了CountDownLatch,那CyclicBarrier和它有什么区别呢?
提示:以下是本篇文章正文内容,下面案例可供参考
一、CyclicBarrier是什么?
CyclicBarrier 也是一种多线程并发控制的实用工具,和 CountDownLatch 一样具有等待计数的功能,但是相比于 CountDownLatch 功能更加强大,因为CountDownLatch是一次性的,而CyclicBarrier是可以循环的。
主要作用使用它进行线程的同步,进行计数循环、重置。
可以理解为,去火锅店吃火锅,吃完一桌走人,再重新来一桌人。
二、主要参数与方法
1.主要参数
参数 | 含义 |
---|---|
parties | 调用的线程数 |
barrierAction | 触发障碍时候执行任务 |
2.方法
//将线程处于休眠状态,所有的线程到达临界点
public int await() throws InterruptedException, BrokenBarrierException
//更新状态,唤醒all
nextGeneration()
//是否处于断开状态
boolean isBroken()
//获取临界点上的线程数
int getNumberWaiting()
//重置为初始状态
reset()
3.构造方法
public CyclicBarrier(int parties) {
this(parties, null);
}
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties;
this.barrierCommand = barrierAction;
}
三、核心方法源码
dowait
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
final ReentrantLock lock = this.lock;
lock.lock();
try {
//生成一个实例
final Generation g = generation;
//broken判断是否为断开,初始化为false
if (g.broken)
throw new BrokenBarrierException();
//判断线程是否终止
if (Thread.interrupted()) {
//如果终止,broken设置为true,唤醒所有
breakBarrier();
throw new InterruptedException();
}
//自减一
int index = --count;
//都到达临界点,出发临界点任务
if (index == 0) { // tripped
boolean ranAction = false;
try {
final Runnable command = barrierCommand;
if (command != null)
command.run();
ranAction = true;
nextGeneration();
return 0;
} finally {
//任务执行完毕,唤醒所有,broken设置为断开
if (!ranAction)
breakBarrier();
}
}
// loop until tripped, broken, interrupted, or timed out
for (;;) {
try {
//timed默认为false,挂起当前任务
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();
}
}
四、实践
同样还是利用四个人吃火锅在作为例子。通过调用await相互等待,到了临界点的时候出发临界点的线程任务。
private static CyclicBarrier cyclicBarrier = new CyclicBarrier(4,() ->{
System.out.println("人到齐了,开吃!");
});
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(4);
IntStream.range(0,4).forEach(i ->{
executorService.submit(() ->{
System.out.println(Thread.currentThread().getName() + "来吃火锅");
try {
cyclicBarrier.await();
System.out.println(Thread.currentThread().getName() + "动筷子");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
});
});
executorService.shutdown();
}
输出结果
1.分享一个网上的使用例子
// 订单队列
Vector<P> pos;
// 派送单队列
Vector<D> dos;
// 执行回调的线程池
Executor executor =
Executors.newFixedThreadPool(1);
final CyclicBarrier barrier =
new CyclicBarrier(2, ()->{
executor.execute(()->check());
});
void check(){
P p = pos.remove(0);
D d = dos.remove(0);
// 执行对账操作
diff = check(p, d);
// 差异写入差异库
save(diff);
}
void checkAll(){
// 循环查询订单库
Thread T1 = new Thread(()->{
while(存在未对账订单){
// 查询订单库
pos.add(getPOrders());
// 等待
barrier.await();
}
});
T1.start();
// 循环查询运单库
Thread T2 = new Thread(()->{
while(存在未对账订单){
// 查询运单库
dos.add(getDOrders());
// 等待
barrier.await();
}
});
T2.start();
}
总结
对比一下CountDownLacth与CyclicBarrier
- CountDownLatch 强调一个线程等多个线程完成某件事情。CyclicBarrier 是多个线程互等,一起完成。
- CountDownLacth是一次性的,而CyclicBarrier 的计数器是可以循环利用的,而且具备自动重置的功能,一旦计数器减到 0 会自动重置到你设置的初始值。
- CyclicBarrier 可以设置回调函数
- 调用 CountDownLatch 的 countDown 方法后,当前线程并不会阻塞,会继续往下执行;而调用 CyclicBarrier 的 await 方法,会阻塞当前线程,直到 CyclicBarrier 指定的线程全部都到达了指定点的时候,才能继续往下执行。