上一篇咱讲了 CountDownLatch 可以解决多个线程同步的问题,相比于 join 来说它的应用范围更广,不仅可以应用在线程上,还可以应用在线程池上。然而 CountDownLatch 却是一次性的计数器,以王者农药来说,咱们不可能一场团战就决定比赛的输赢,所以在某些场景下,咱们是需要重复使用某个等待功能的,这就是我们今天要介绍的另一个主角——CyclicBarrier。

CyclicBarrier

CyclicBarrier 翻译为中文是循环(Cyclic)栅栏(Barrier)的意思,它的大概含义是实现一个可循环利用的屏障。



CyclicBarrier 作用是让一组线程相互等待,当达到一个共同点时,所有之前等待的线程再继续执行,且 CyclicBarrier 功能可重复使用。

举个栗子

比如磊哥要坐班车回老家,因为中途不允许上、下乘客,所以营运的公司为了收益最大化,就会等人满之后再发车。像这种等人坐满就发一班车的场景,就是 CyclicBarrier 所擅长的,因为它可以重复使用(不像 CountDownLatch 那样只能用一次)。

CyclicBarrier VS CountDownLatch

CountDownLatch:一个或者多个线程,等待另外 N 个线程完成某个事情之后才能执行。

CountDownLatch 就像玩王者农药开局的加载一样,所有人要等待其他人都加载 100% 之后才能开始游戏。

CyclicBrrier:N 个线程相互等待,直到有足够数量的线程都到达屏障点之后,之前等待的线程就可以继续执行了。

CyclicBrrier 就像老司机开车一样,如果车上还有空余的座位,那么所有人都得等着,直到座位被坐满之后,老司机才会发车。

CyclicBarrier使用

import java.util.Date;
import java.util.Random;
import java.util.concurrent.*; public class CyclicBarrierExample {
public static void main(String[] args) {
// 创建 CyclicBarrier
final CyclicBarrier cyclicBarrier = new CyclicBarrier(2, new Runnable() {
@Override
public void run() {
System.out.println("人满了,准备发车:" + new Date());
}
}); // 线程调用的任务
Runnable runnable = new Runnable() {
@Override
public void run() {
// 生成随机数 1-3
int randomNumber = new Random().nextInt(3) + 1;
// 进入任务
System.out.println(String.format("我是:%s 再走:%d 秒就到车站了,现在时间:%s",
Thread.currentThread().getName(), randomNumber, new Date()));
try {
// 模拟执行
TimeUnit.SECONDS.sleep(randomNumber);
// 调用 CyclicBarrier
cyclicBarrier.await();
// 任务执行
System.out.println(String.format("线程:%s 上车,时间:%s",
Thread.currentThread().getName(), new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}; // 创建线程池
ExecutorService threadPool = Executors.newFixedThreadPool(10);
// 执行任务 1
threadPool.submit(runnable);
// 执行任务 2
threadPool.submit(runnable);
// 执行任务 3
threadPool.submit(runnable);
// 执行任务 4
threadPool.submit(runnable); // 等待所有任务执行完终止线程池
threadPool.shutdown();
}
}

以上代码执行结果如下:



从上述结果可以看出:当 CyclicBarrier 的计数器设置为 2 时,线程 2 和 线程 3 都到屏障点之后,老司机才会发第一波车,再 2s 之后,线程 1 和线程 4 也同时进入了屏障点,这时候老司机又可以再发一波车了。

实现原理

我们先来看下 CyclicBarrier 的类图:



由上图可知 CyclicBarrier 是基于独占锁 ReentrantLock 实现的,其底层也是基于 AQS 的。

在 CyclicBarrier 类的内部有一个计数器 count,当 count 不为 0 时,每个线程在到达屏障点会先调用 await 方法将自己阻塞,此时计数器会减 1,直到计数器减为 0 的时候,所有因调用 await 方法而被阻塞的线程就会被唤醒继续执行。当 count 计数器变成 0 之后,就会进入下一轮阻塞,此时 parties(parties 是在 new CyclicBarrier(parties) 时设置的值)会将它的值赋值给 count 从而实现复用。

常用方法

CyclicBarrier(parties):初始化相互等待的线程数量的构造方法。

CyclicBarrier(parties,Runnable barrierAction):初始化相互等待的线程数量以及屏障线程的构造方法,当 CyclicBarrier 的计数器变为 0 时,会执行 barrierAction 构造方法。

getParties():获取 CyclicBarrier 打开屏障的线程数量,也称为方数。

getNumberWaiting():获取正在CyclicBarrier上等待的线程数量。

await():在 CyclicBarrier 上进行阻塞等待,直到发生以下情形之一:

在 CyclicBarrier 上等待的线程数量达到 parties,则所有线程被释放,继续执行;

  • 当前线程被中断,则抛出 InterruptedException 异常,并停止等待,继续执行;
  • 其他等待的线程被中断,则当前线程抛出 BrokenBarrierException 异常,并停止等待,继续执行;
  • 其他等待的线程超时,则当前线程抛出 BrokenBarrierException 异常,并停止等待,继续执行;
  • 其他线程调用 CyclicBarrier.reset() 方法,则当前线程抛出 BrokenBarrierException 异常,并停止等待,继续执行。

await(timeout,TimeUnit):在CyclicBarrier上进行限时的阻塞等待,直到发生以下情形之一:

  • 在 CyclicBarrier 上等待的线程数量达到 parties,则所有线程被释放,继续执行;
  • 当前线程被中断,则抛出 InterruptedException 异常,并停止等待,继续执行;
  • 当前线程等待超时,则抛出 TimeoutException 异常,并停止等待,继续执行;
  • 其他等待的线程被中断,则当前线程抛出 BrokenBarrierException 异常,并停止等待,继续执行;
  • 其他等待的线程超时,则当前线程抛出 BrokenBarrierException 异常,并停止等待,继续执行;
  • 其他线程调用 CyclicBarrier.reset() 方法,则当前线程抛出 BrokenBarrierException 异常,并停止等待,继续执行。

isBroken():获取是否破损标志位 broken 的值,此值有以下几种情况:

  • CyclicBarrier 初始化时,broken=false,表示屏障未破损;
  • 如果正在等待的线程被中断,则 broken=true,表示屏障破损;
  • 如果正在等待的线程超时,则 broken=true,表示屏障破损;
  • 如果有线程调用 CyclicBarrier.reset() 方法,则 broken=false,表示屏障回到未破损状态。

reset():使得CyclicBarrier回归初始状态,直观来看它做了两件事:

  • 如果有正在等待的线程,则会抛出 BrokenBarrierException 异常,且这些线程停止等待,继续执行。
  • 将是否破损标志位 broken 置为 false。

总结

CyclicBrrier 是通过独占锁 ReentrantLock 实现计数器的原子性更新的,CyclicBrrier 最常用的是 await() 方法,使用此方法会将计数器 -1,并判断当前的计数器是否为 0,如果不为 0 就会阻塞等待,并计时器为 0 之后,才能继续执行剩余任务。CyclicBrrier 相比于 CountDownLatch 来说,它的优势在于可以重复使用。

参考 & 鸣谢

blog.csdn.net/qq_39241239/article/details/87030142

blog.csdn.net/zzg1229059735/article/details/61191679

www.cnblogs.com/yaochunhui/p/13494689.html

关注公众号:「Java中文社群」查看更多精彩内容。

CyclicBarrier:人齐了,老司机就可以发车了!的更多相关文章

  1. 【Bugly干货分享】老司机教你 “飙” EventBus 3

    Bugly 技术干货系列内容主要涉及移动开发方向,是由 Bugly 邀请腾讯内部各位技术大咖,通过日常工作经验的总结以及感悟撰写而成,内容均属原创,转载请标明出处. EventBus对于Android ...

  2. 优测优社区干货精选|老司机乱谈编辑器之神——vim

    文 / 腾讯 吴双 前言 优测小优 有话说: 腾讯优测只有应用测试大神?不不不,我们还有各种研发大牛! *** vim 是一种信仰,我自从2004年有了这个信仰,已经12个年头了.本文介绍了学习vim ...

  3. 老司机告诉你高质量的Java代码是怎么练成的?

    一提起程序员,首先想到的一定是"码农",对,我们是高产量的优质"码农",我们拥有超跃常人的逻辑思维以及不走寻常路的分析.判别能力,当然,我们也有良好的编码规范, ...

  4. 老司机在zabbix上的一次翻车

    [前言] 自以为是zabbix的老司机了,没有想到今天翻车了! 一般人出错了都可以找到一个借口.我就不一样啦,我感觉我可以找两个1): 针对官方文档 给出的操作步骤没有经过深入的思考 2): 今天没有 ...

  5. javbus爬虫-老司机你值得拥有

    # 起因 有个朋友叫我帮忙写个爬虫,爬取javbus5上面所有的详情页链接,也就是所有的https://www.javbus5.com/SRS-055这种链接, 我一看,嘿呀,这是司机的活儿啊,我绝对 ...

  6. 年薪30W的软件测试“老司机”工作经验

    这几天,新入职的小MM提议“老司机”们把自己这些年的软件测试工作经验跟大家分享一下,让新同学学习学习,利用空闲时间我整理了一些,可能不全,勉强看看,这也算是对自己这些年的工作总结. 测试阶段划分 1. ...

  7. 汽车AC键到底是干什么的?老司机告诉你

    现在很多人都会开车,想我当初学车的时候一会就可以上手了,开车简单,但是很多细节方面的就是得慢慢学习的过程,比如说汽车的AC键,我相信很多车主,包括老司机都不知道到底有哪些作用,只知道开空调,其实它的用 ...

  8. 有容云:上车 | 听老司机谈Docker安全合规建设

    编者注: 本文根据7月19日DockOne社群分享内容整理而成,分享嘉宾蒋运龙,有容云高级咨询顾问,一个IT的老兵,十年来混迹于存储.三网融合.多屏互动.智能穿戴.第三方支付.Docker等行业:经历 ...

  9. zz“老司机”成长之路:自动驾驶车辆调试实践

    随着自动驾驶技术的发展,一辆新车从被改装到上路需要经过的调试流程也有了许多提升.今天,我希望结合自己之前的调车经验来跟大家分享一下我们是如何将系统的各个模块逐步上车.调试.集成,进而将一辆“新手”车培 ...

随机推荐

  1. 12月15日BGV币行情分析

    今日,DeFi市场格外精彩.各主流概念币种走势出现了涨跌各半的两极态势.笔者认为,由于并没有总体可以利好DeFi市场的基本面因素,所以各DeFi概念币种的涨跌态势,还是与各自的基本面和技术面走势相关. ...

  2. 为什么要抢挖Baccarat流动性挖矿的头矿?头矿的价值是什么?

    今年下半年,DeFi流动性挖矿非常受投资者的欢迎,究其原因,其超高的挖矿回报率着实足够吸引无数投资者的眼球.而即将上线的Baccarat流动性挖矿,也未上线先火了一把.Baccarat是由NGK公链推 ...

  3. pycharm + git+gitlab的可视化界面操作

    前言: 写这篇博​​客,主要为了记录一套经过本人实践,并运行通过的操作gitlab流程. 通过以下步骤,可实现最基本的远程服务器(gitlab)和本地工具(pycharm)的,针对两端文件增删改查的及 ...

  4. 关于Python 编码的一点认识

    在计算机中,所有数据的存储.运算以及传输都必须是二进制数字,因为计算机只认识0和1. 当一个人把一份数据传给另一个人时,计算机传递的是其实是二进制数字,但这些数字需要被还原为原始信息. 这个工作当然是 ...

  5. TERSUS无代码开发(笔记04)-CSS样式设置

    CSS样式设置 1.常用显示样式 大小尺寸 说明  间距边距 说明  各类颜色 说明  width 宽 margin 外边距         color  颜色        height 高 pad ...

  6. HTTPS:网络安全攻坚战

    本文为<三万长文50+趣图带你领悟web编程的内功心法>第五个章节. 5.HTTPS 我们知道,明文传输和不安全是HTTP的其中一个特点,但是随着越来越多机密的业务交易转移到线上,如银行转 ...

  7. abp中多种登陆用户的设计

    项目地址:https://gitee.com/bxjg1987/abp 场景 在<学校管理系统>中,学生.家长.教师.教务都可能登陆,做一些属于他们自己的操作.这些用户需要的属性各不相同, ...

  8. win10使用cmd命令关闭防火墙

    在搜索框内输入cmd,右键选择管理员运行 然后输入: NetSh Advfirewall set allprofiles state off #关闭防火墙 Netsh Advfirewall show ...

  9. Nginx常用内核参数优化,安装,基本命令

    1.内核参数配置,默认的Linux内核参数考虑的是通用的场景,明显不符合用于支持高并发访问web服务的定义,所以需要修改Linux内核参数,使得Nginx可以拥有更高的性能.可以通过修改 /etc/s ...

  10. sap2000v21安装教程(附详细安装步骤+中文安装包)

    sap2000 v21是sap2000系列软件的全新版本,也是目前行业中的一款用于结构分析和设计的集成软件,该软件保持了原有产品的传统,具有完善.直观和灵活的界面,能够在交通运输.工业.公共事业.体育 ...