作者 | 王磊

来源 | Java中文社群(ID:javacn666)

转载请联系授权(微信ID:GG_Stone)

上一篇咱讲了 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) {// 创建 CyclicBarrierfinal CyclicBarrier cyclicBarrier = new CyclicBarrier(2, new Runnable() {@Overridepublic void run() {System.out.println("人满了,准备发车:" + new Date());}});// 线程调用的任务Runnable runnable = new Runnable() {@Overridepublic void run() {// 生成随机数 1-3int 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);// 调用 CyclicBarriercyclicBarrier.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);// 执行任务 1threadPool.submit(runnable);// 执行任务 2threadPool.submit(runnable);// 执行任务 3threadPool.submit(runnable);// 执行任务 4threadPool.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

CyclicBarrier:人齐了,老司机就发车了!相关推荐

  1. html懒人编辑器,前端老司机分享——五个前端代码编辑器

    工欲善其事,必先利其器.但我最开始接触网页的时候,人们普遍认为能用记事本写HTML和CSS的才是大神. 那时也没有前端这一说法,随着网页越来越复杂用记事本的开发效率就太低了,也就有了前端代码编辑器的出 ...

  2. Word排版怎么都对不齐,老司机来教你一招,超实用!

    转自:https://www.pinlue.com/article/2021/05/1711/4511604527957.html

  3. 鸿蒙汽修大明路店,为啥很多车主在4S店首保之后都会选择汽修厂保养?老司机说出原因...

    虽然说现在汽车的价格越来越便宜了,但是一辆普通的汽车少说也要十万块,现在的消费水平如此高,很多车主都抱怨到能买得起车却开不起,汽车买回来到处都要花钱,上路之前要买保险,上路之后要加油,另外还需要保养等 ...

  4. html5什么网站知乎,老司机秒懂的5个资源网站,知乎超过10万人推荐,竟被我挖掘到了...

    想要和别人拉开差距?不想安于现状?那就赶紧打破这种安逸的环境,抽出时间赶紧学习!这5个老司机秒懂的资源网站,在知乎热搜上排名第三,阅读量都超过1800万了!居然在这里被我挖掘到了! 一.Pexels ...

  5. 老司机都懂的x件事,一般人我不告诉他

    临近双十一, 小编还寻思着不知道买点啥的时候, 室友忽然告诉我ta!买!车!了! ? 忽然间脑海里就闪现过秋名山车神的种种名场面-- <头文字D> 来源:知乎@飞隅 开始盘算哪天一起出去自 ...

  6. 分享7个比B站更刺激的老司机网站,别轻易点开

    俗话说摸鱼一时爽,一直摸一直爽,作为一个程序员老司机了,一头乌黑浓密的头发还时不时被同事调侃,就靠这10个网站让我健康生活,不建议经常性使用,因为还有一句俗话,那就是"摸鱼一时爽,没有年终奖 ...

  7. 十年建站老司机带你十分钟搭建网站

    十年建站老司机带你十分钟搭建网站 本文概要: 1. 域名 + 域名注册 + 域名备案 + 域名解析绑定 2. 服务器 + 虚拟主机 + windows服务器 + linux服务器 3. CMS建站系统 ...

  8. rust矿洞绳子怎么爬下_车底下绑一根绳子妙用在哪?看看老司机怎么说!

    汽车的出现提高了我们工作和生活的效率,但汽车也是一个"伤人利器",如果不好好了解关于汽车的一些知识,在使用的过程中就可能会出现一些错误操作,导致汽车和我们自身都受到伤害:所以老司机 ...

  9. 读论文七步走!CV老司机万字长文:一篇论文需要读4遍

      视学算法报道   编辑:LRS [新智元导读]读论文对于AI新手和工程师来说可能是一件比较难的事.最近一位从业超5年的CV老司机发布了一篇万字长文,讲述了读论文七步法,从找论文到总结,每篇论文由浅 ...

最新文章

  1. python中常见的数据类型_Python中常见的数据类型总结(四)
  2. 记录ie暂时遇到的问题
  3. reactjs创建虚拟DOM的两种方式:使用jsx和js创建虚拟DOM
  4. JMS ActiveMQ案例
  5. C++版二叉树非递归遍历
  6. Mysql CURD复习(数据库、表、数据)
  7. 2018.09.15 vijos1053Easy sssp(最短路)
  8. java个人中心修改界面怎么整_怎么对个人中心页面访问进行控制
  9. 北方民族大学c语言期末考试试题,2018年北方民族大学软件工程832C语言程序设计与数据结构之C程序设计考研核心题库...
  10. C语言 扫雷游戏(代码+注释)
  11. 猎豹网校c语言,[猎豹网校]数据结构与算法_C语言
  12. java cas单点登录_JAVA - 登录 单点登录 cas
  13. 膝关节前交叉韧带重建术后取钉记
  14. 一款基于 Spring Boot 的公众号管理系统,已开源,别再自己写了!
  15. 苹果MAC AIR MACOS X 10.8安装notes提示OS版本过低
  16. 最新青龙拉库命令及监控变量教学
  17. 在GitHub/Gitee上,搭建一个简单的所见即所得博客
  18. Baseline Wander Correction: 基线漂移补偿
  19. 同一wifi下小米电视无法投屏
  20. 电商后台设计:商品维护

热门文章

  1. pythonwrite连续写入_python文件写入write()的操作
  2. 【Java从入门到头秃专栏 】(三) 控制流程 Math Date DateFormat Calendar System BigDecimal Random
  3. Problem D: 删出多余的空格
  4. Shell编程—企业生产案例
  5. 暴风TV请来中国人工智能first lady冯雁教授任首席科学家
  6. Ubuntu 忘记密码的处理方法
  7. WEB平台架构之:LAMP(Linux+Apache+MySQL+PHP)
  8. Netbackup 7.6新增功能和优势
  9. Oracle SQL语句执行步骤
  10. 你所应该知道的云计算