文章目录

  • 前言
  • 什么是CyclicBarrier
  • CyclicBarrier原理
  • CyclicBarrier VS CountDownLatch
    • CountDownLatch图示:
    • CyclicBarrier图示:
    • 两者的异同:
  • CyclicBarrier核心源码
  • 实战演示
    • 1、创建测试demo
    • 2、创建测试用例
    • 3、查看测试结果
  • 写在最后

前言

前面我们分享了线程等待其他线程执行同步类CountDownLatch,CountDownLatch是一个或多个线程等待其他线程执行完成才执行,比如主线程等待其他多个子线程执行完成再执行主线程逻辑。但是,在实际生产开发过程中有些场景需要多个线程相互等待,比如合并计算结果、比赛计时等等,这个时候我们就需要用到循环屏障Cyclicbarrier。

什么是CyclicBarrier

CyclicBarrier翻译为循环屏障,正如其名它是一个可以循环使用,可以让一组线程相互等待,线程只有全部到达屏障才能使屏障消失的一个同步辅助类。

CyclicBarrier原理

CyclicBarrier内部使用可重入ReentrantLock保证线程同步(并发编程之可重入锁ReentrantLock),使用Condition来保证线程阻塞隔离和唤醒,使用组parties来保存可重用副本个数,使用–count来计算还没有到达屏障的线程数目。在实际的运行过程中,CyclicBarrier会使用count值模拟一个屏障,count > 0则表示还有没有到达屏障的线程,此时当前线程需要加入阻塞;count == 0则表示所有线程都已经到达屏障点,此时会重置CyclicBarrier为下次重用做准备并唤醒所有阻塞线程。

CyclicBarrier VS CountDownLatch

CountDownLatch图示:

CyclicBarrier图示:

两者的异同:

1、CyclicBarrier 与 CountDownLatch 都是同步辅助类,都达到了线程同步的效果;
2、CyclicBarrier 是多个线程相互等待,CountDownLatch则是一个或多个等待其他线程;
3、CyclicBarrier 内部使用可重入锁ReentrantLock保证同步,CountDownLatch 基于抽象同步队列AQS,由于ReentrantLock 也是基于AQS实现,故两者的同步逻辑都差距不大。

CyclicBarrier核心源码

我们进入JUC CyclicBarrier 核心源码:

//重置cyclicbarrier
private void nextGeneration() {// signal completion of last generationtrip.signalAll();// set up next generationcount = 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;if (index == 0) {  // trippedboolean ranAction = false;try {final Runnable command = barrierCommand;if (command != null)command.run();ranAction = true;//重置屏障并唤醒所有阻塞线程nextGeneration();return 0;} finally {if (!ranAction)breakBarrier();}}// 线程自旋加入阻塞并增加超时逻辑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();}
}

如上源码所示,我们对主要的步骤做了相对的注释。
CyclicBarrier的核心部分就是dowait()方法,我们外部调用的await()方法实际就是调用的这个方法。
dowait()方法内部使用可重入锁ReentrantLock保证线程同步,该方法的主要逻辑是:
1、首先会验证当前屏障是否已经打破,当前线程是否已经被打断,如果已经被打破或者打断则直接会释放所有阻塞线程;
2、然后会将计数器–count,如果count == 0 则表示所有线程到达屏障点,此时会先执行构造方法传入的线程,后调用nextGeneration()方法重置CyclicBarrier并唤醒所有阻塞线程;如果count > 0 则表示还有线程没有到达屏障,则当前线程自旋加入阻塞。

实战演示

之前参加过的一道面试机试题目:请模拟出赛马比赛中各个赛马到达终点的时间。

这个题目的场景其实就是要让每个赛马都准备好了发令开始计时,然后各个赛马根据自身的情况完成比赛给出结束时间。这里我们可以用CyclicBarrier 模拟赛马开跑的时候,然后我们随机让线程睡眠模拟赛马跑步时间,最后打印出各个赛马到达的时间即可。

1、创建测试demo

/*** CyclicBarrier模拟赛马比赛* @author senfel* @version 1.0* @date 2023/5/8 11:27*/
public class CyclicBarrierDemo {/*** 比赛方法* @author senfel* @date 2023/5/8 11:29* @return void*/public static void matchFun() throws Exception{//线程池模拟赛马池ExecutorService executorService = Executors.newFixedThreadPool(10);//cyclicBarrier 屏障模拟发令CyclicBarrier cyclicBarrier = new CyclicBarrier(5);//时间格式化DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss");//模拟比赛结束CountDownLatch countDownLatch = new CountDownLatch(5);//模拟比赛,每场比赛5匹赛马for(int i=0;i<5;i++){executorService.execute(new Runnable() {@Overridepublic void run() {//赛马等待发令System.err.println("赛马:"+Thread.currentThread().getName()+"等待发令");try {cyclicBarrier.await();LocalDateTime startTime = LocalDateTime.now();System.err.println("赛马:"+Thread.currentThread().getName()+"开始起跑,起跑时间为:"+ startTime.format(dateTimeFormatter));//线程随机睡眠模拟赛马赛跑时间int time = new Random().nextInt(20)  * 1000;Thread.sleep(time);LocalDateTime endTime = LocalDateTime.now();Duration between = Duration.between(startTime, endTime);long millis = between.toMillis();System.err.println("赛马:"+Thread.currentThread().getName()+"完成比赛," +"起跑时间为:"+startTime.format(dateTimeFormatter)+"," +"到达时间为:"+endTime.format(dateTimeFormatter)+"," +"耗时:"+millis+"毫秒。");countDownLatch.countDown();} catch (InterruptedException e) {e.printStackTrace();} catch (Exception e) {e.printStackTrace();}}});}countDownLatch.await();System.err.println("比赛完成");executorService.shutdown();}
}

2、创建测试用例

@SpringBootTest
class DemoApplicationTests {

/*** 模拟赛马测试用例* @author senfel* @date 2023/5/8 13:42* @return void*/
public void matchTest() throws Exception{CyclicBarrierDemo.matchFun();
}

}

3、查看测试结果

赛马:pool-1-thread-2等待发令
赛马:pool-1-thread-1等待发令
赛马:pool-1-thread-3等待发令
赛马:pool-1-thread-5等待发令
赛马:pool-1-thread-4等待发令

赛马:pool-1-thread-3开始起跑,起跑时间为:12:33:51
赛马:pool-1-thread-1开始起跑,起跑时间为:12:33:51
赛马:pool-1-thread-2开始起跑,起跑时间为:12:33:51
赛马:pool-1-thread-4开始起跑,起跑时间为:12:33:51
赛马:pool-1-thread-5开始起跑,起跑时间为:12:33:51

赛马:pool-1-thread-2完成比赛,起跑时间为:12:33:51,到达时间为:12:33:55,耗时:4002毫秒。
赛马:pool-1-thread-1完成比赛,起跑时间为:12:33:51,到达时间为:12:33:57,耗时:6002毫秒。
赛马:pool-1-thread-3完成比赛,起跑时间为:12:33:51,到达时间为:12:33:58,耗时:7002毫秒。
赛马:pool-1-thread-4完成比赛,起跑时间为:12:33:51,到达时间为:12:34:03,耗时:12002毫秒。
赛马:pool-1-thread-5完成比赛,起跑时间为:12:33:51,到达时间为:12:34:10,耗时:19002毫秒。

比赛完成

写在最后

CyclicBarrier循环屏障是多线程并发编程中的常用同步类,我们可以用它来实现多个线程相互等待,并且可重用。其中的dowait()方式是核心方法,用可重入锁ReentrantLock保证了方法逻辑同步功能,使用Condition的await()、signalAll()方法来阻塞和唤醒线程。

⭐️路漫漫其修远兮,吾将上下而求索

并发编程之循环屏障CyclicBarrier相关推荐

  1. Java并发工具类-循环屏障CyclicBarrier

    CyclicBarrier简介 CyclicBarrier API 构造方法 await方法 reset方法 使用样例 CyclicBarrier源码详解 CyclicBarrier中属性 构造方法及 ...

  2. Java多线程编程-(6)-两种常用的线程计数器CountDownLatch和循环屏障CyclicBarrier

    前几篇: Java多线程编程-(1)-线程安全和锁Synchronized概念 Java多线程编程-(2)-可重入锁以及Synchronized的其他基本特性 Java多线程编程-(3)-线程本地Th ...

  3. Java并发编程:CountDownLatch、CyclicBarrier和Semaphore

    2019独角兽企业重金招聘Python工程师标准>>> Java并发编程:CountDownLatch.CyclicBarrier和Semaphore 在java 1.5中,提供了一 ...

  4. java并发编程同步器 Semaphore、CyclicBarrier、Exchanger、CountDownLatch

    为什么80%的码农都做不了架构师?>>>    一.Semaphore(信号量) 注解:信号量,其实就是定义一定的数量,只有释放一个才能进去下一个,其余都得进入等待状态.比如有2个洗 ...

  5. Java并发编程:CountDownLatch、CyclicBarrier和 Semaphore

    2019独角兽企业重金招聘Python工程师标准>>> 在java 1.5中,提供了一些非常有用的辅助类来帮助我们进行并发编程,比如CountDownLatch,CyclicBarr ...

  6. java 多线程分段等待执行完成状况,循环屏障CyclicBarrier | Java工具类

    目录 前言 Maven依赖 代码 总结 前言 工作中是否有这样的场景,多个线程任务,如果所有线程完成到某个阶段,你希望知道所有线程均完成该阶段.当然你使用线程计数可以实现,只是不够优雅. 所以我即:j ...

  7. java循环屏障,Java并发编程系列23|循环屏障CyclicBarrier

    本文转载自[微信公众号:java进阶架构师,ID:java_jiagoushi]经微信公众号授权转载,如需转载与原文作者联系 本篇介绍第二个并发工具类CyclicBarrier,CyclicBarri ...

  8. Java并发编程(十六):CyclicBarrier源码分析

    前言   CyclicBarrier可以建立一个屏障,这个屏障可以阻塞一个线程直到指定的所有线程都达到屏障.就像团队聚餐,等所有人都到齐了再一起动筷子.根据Cyclic就可以发现CyclicBarri ...

  9. 【并发编程】CountDownLatch与CyclicBarrier

    CountDownLatch 简介 CountDownLatch,意思是倒数门闩.它的作用是多个线程做汇聚.主线程开启了 A.B.C 三个线程做不同的事情,但是主线程需要等待 A.B.C 三个线程全部 ...

最新文章

  1. 【驱动】在内核源码中添加驱动程序
  2. 心得丨深度学习的技术原理、迭代路径与局限
  3. 在program A里访问program B local class的两种方式
  4. mfc控件位置随对话框窗口移动
  5. python typing optional_python类型检测最终指南--Typing模块的使用
  6. 图像通道变换python-opencv
  7. HTTPS 防劫持攻击
  8. UI设计师(界面设计)面试题
  9. stm32蜂鸣器程序
  10. DIV+CSS页面布局
  11. KIS专业版12.0迁移K3WISE14.3后自定义核算项目权限问题
  12. django配置(setting)之ALLOWED_HOSTS
  13. 微信公众平台开发实战
  14. LCD屏幕,IPS屏幕,TFT屏幕,SLCD屏幕和AMOLED手机屏幕介
  15. DM36x Rate Control Modes
  16. Unity游戏脚本简单学习
  17. Tableau图表:气泡图,文字云,树状图等
  18. 目标检测YOLO实战应用案例100讲-基于深度学习的交通场景多尺度目标检测算法研究与应用
  19. 《深入理解JAVA虚拟机》周志明 第三版 - 第二章 JAVA内存区域与内存溢出异常
  20. 纯干货!Java后端开发十二条经验分享!

热门文章

  1. excel生成动态进度条
  2. 计算机怎么删除东西,电脑文件如何删除干净
  3. 用word2003打开.docx文件
  4. 云聚高性能,论道“新超算”
  5. 自动化测试工具 Java等
  6. 虚拟机重启网卡命令和防火墙关闭和开启
  7. 中国科学院深圳先进技术研究院合成所赵国屏研究员课题组2022年招聘启事
  8. java左手画圆右手画方_左手画圆、右手画方,双手齐用同时养护、开发你的左右大脑!...
  9. 防抖为什么要使用闭包
  10. explain的使用