CyclicBarrier是一个用于线程同步的辅助类,它允许一组线程等待彼此,直到所有线程都到达集合点,然后执行某个设定的任务。

举个例子:几个人约定了某个地方集中,然后一起出发去旅行。每个参与的人就是一个线程,CyclicBarrier就是那个集合点,所有人到了之后,就一起出发。

CyclicBarrier的构造函数有两个:

  1. // parties是参与等待的线程的数量,barrierAction是所有线程达到集合点之后要做的动作
  2. public CyclicBarrier(int parties, Runnable barrierAction);
  3.  
  4. // 达到集合点之后不执行操作的构造函数
  5. public CyclicBarrier(int parties)

CyclicBarrier只是记录线程的数目,CyclicBarrier是不创建任何线程的。线程是通过调用CyclicBarrier的await方法来等待其他线程,如果调用await方法的线程数目达到了预设值,也就是上面构造方法中的parties,CyclicBarrier就会开始执行barrierAction。

因此我们来看CyclicBarrier的核心方法dowait,也就是await方法调用的私有方法:

  1. private int dowait(boolean timed, long nanos)
  2. throws InterruptedException, BrokenBarrierException,
  3. TimeoutException {
  4. final ReentrantLock lock = this.lock;
  5. lock.lock();
  6. try {
  7. final Generation g = generation;
  8.  
  9. if (g.broken)
  10. throw new BrokenBarrierException();
  11.  
  12. if (Thread.interrupted()) {
  13. breakBarrier();
  14. throw new InterruptedException();
  15. }
  16. // count就是预设的parties,count减1的值表示还剩余几个
  17. // 线程没有达到该集合点
  18. int index = --count;
  19. // index为0表示所有的线程都已经达到集合点,这时
  20. // 占用最后一个线程,执行运行设定的任务
  21. if (index == 0) {
  22. boolean ranAction = false;
  23. try {
  24. final Runnable command = barrierCommand;
  25. if (command != null)
  26. command.run();
  27. ranAction = true;
  28. // 唤醒其他等待的线程,
  29. // 更新generation以便下一次运行
  30. nextGeneration();
  31. return 0;
  32. } finally {
  33. // 如果运行任务时发生异常,设置状态为broken
  34. // 并且唤醒其他等待的线程
  35. if (!ranAction)
  36. breakBarrier();
  37. }
  38. }
  39.  
  40. // 还有线程没有调用await,进入循环等待直到其他线程
  41. // 达到集合点或者等待超时
  42. for (;;) {
  43. try {
  44. // 如果没有设置超时,进行无超时的等待
  45. if (!timed)
  46. trip.await();
  47. // 有超时设置,进行有超时的等待
  48. else if (nanos > 0L)
  49. nanos = trip.awaitNanos(nanos);
  50. } catch (InterruptedException ie) {
  51. // generation如果没有被更新表示还是当前的运行
  52. // (generation被更新表示集合完毕并且任务成功),
  53. // 在状态没有被设置为broken状态的情况下,遇到线程
  54. // 中断异常表示当前线程等待失败,需要设置为broken
  55. // 状态,并且抛出中断异常
  56. if (g == generation && ! g.broken) {
  57. breakBarrier();
  58. throw ie;
  59. } else {
  60. // else对应的条件为:g != generation || g.broken
  61. // 表示要么generation已经被更新意味着所有线程已经到达
  62. // 集合点并且任务执行成功,要么就是是broken状态意味着
  63. // 任务执行失败,无论哪种情况所有线程已经达到集合点,当
  64. // 前线程要结束等待了,发生了中断异常,需要中断当前线程
  65. // 表示遇到了中断异常。
  66. Thread.currentThread().interrupt();
  67. }
  68. }
  69.  
  70. // 如果发现当前状态为broken,抛出异常
  71. if (g.broken)
  72. throw new BrokenBarrierException();
  73. // generation被更新表示所有线程都已经达到集合点
  74. // 并且预设任务已经完成,返回该线程进入等待顺序号
  75. if (g != generation)
  76. return index;
  77. // 等待超时,设置为broken状态并且抛出超时异常
  78. if (timed && nanos <= 0L) {
  79. breakBarrier();
  80. throw new TimeoutException();
  81. }
  82. }
  83. } finally {
  84. lock.unlock();
  85. }
  86. }

1. 任何一个线程等待时发生异常,CyclicBarrier都将被设置为broken状态,运行都会失败

2. 每次运行成功之后CyclicBarrier都会清理运行状态,这样CyclicBarrier可以重新使用

3. 对于设置了超时的等待,在发生超时的时候会引起CyclicBarrier的broken

源码阅读 - java.util.concurrent (四)CyclicBarrier的更多相关文章

  1. 源码阅读 - java.util.concurrent (一)

    java.util.concurrent这个包大致可以分为五个部分: Aomic数据类型 这部分都被放在java.util.concurrent.atomic这个包里面,实现了原子化操作的数据类型,包 ...

  2. 源码阅读 - java.util.concurrent (三)ConcurrentHashMap

    在java.util.concurrent包中提供了一个线程安全版本的Map类型数据结构:ConcurrentMap.本篇文章主要关注ConcurrentMap接口以及它的Hash版本的实现Concu ...

  3. 源码阅读 - java.util.concurrent (二)CAS

    背景 在JDK 5之前Java语言是靠synchronized关键字保证同步的,这会导致有锁 锁机制存在以下问题: (1)在多线程竞争下,加锁.释放锁会导致比较多的上下文切换和调度延时,引起性能问题. ...

  4. Java源码之 java.util.concurrent 学习笔记01

    准备花点时间看看 java.util.concurrent这个包的源代码,来提高自己对Java的认识,努力~~~ 参阅了@梧留柒的博客!边看源码,边通过前辈的博客学习! 包下的代码结构分类: 1.ja ...

  5. 源码(09) -- java.util.Arrays

    java.util.Arrays 源码分析 ------------------------------------------------------------------------------ ...

  6. 如何阅读Java源码 阅读java的真实体会

    刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动. 源码阅读,我觉得最核心有三点:技术基础+强烈的求知欲+耐心.   说到技术基础,我打个比 ...

  7. 源码(03) -- java.util.Collection<E>

     java.util.Collection<E> 源码分析(JDK1.7) -------------------------------------------------------- ...

  8. JDK1.8源码(五)——java.util.Vector类

    JDK1.8源码(五)--java.lang. https://www.cnblogs.com/IT-CPC/p/10897559.html

  9. JDK1.8源码(六)——java.util.LinkedList 类

    上一篇博客我们介绍了List集合的一种典型实现 ArrayList,我们知道 ArrayList 是由数组构成的,本篇博客我们介绍 List 集合的另一种典型实现 LinkedList,这是一个有链表 ...

随机推荐

  1. Cocos2d-x 3.0final 终结者系列教程09-漆节点Node中间Schedule

    怎么做HelloWorld工程HelloWorld文字实现它自己主动运动? 有的童鞋会想到使用线程.不断的变化Label的Position, 不要那样做,因为Cocos2d-x在主线程只能被改变Nod ...

  2. apt-spy 软件源更新

    ebian上的apt-get是最快的软件安装方式,不过要用好apt-get,首先得需要找到最快的源,这样安装软件的时候才能获得好的速度,用起来才能得心应手. 有的源在用了一段以后,就会失效,这个时候, ...

  3. sql 多列求和

    列相加即可注意Null不可加,先用ISNULL方法验证,设置默认值 SELECT ID, Name, Province, City, District, ISNULL(row1, 0), ISNULL ...

  4. WPF 数据模板的使用

    <Window x:Class="CollectionBinding.MainWindow"        xmlns="http://schemas.micros ...

  5. Sqlite在.NET下的使用和Sqlite数据库清理

    原文:Sqlite在.NET下的使用和Sqlite数据库清理 Sqlite 是一款轻量级的关系型数据库,她的好处我就不详细道来了.本文的初衷是为.net平台的使用者提供帮助. Sqlite有专门为VS ...

  6. linux下计划任务学习记录

    0x01 计划任务简介 linux 中计划任务主要分为”循环执行”和”只执行一次”两种,分别对应的时 crond 服务 和 atd 服务: 0x02 只执行一次的计划任务 0x02.1 atd 服务说 ...

  7. Directory.GetFiles()获取多个类型格式的文件

    第一种(用通配符) string[] fileNameX = Directory.GetFiles(@"D:\Sjdc", "*.xls?"); Array a ...

  8. 一键彻底关闭Win10自带Windows Defender杀毒软件

    1.以管理员身份打开系统的命令提示符[cmd.exe]. 2.输入以下命令: reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\ ...

  9. 零元学Expression Blend 4 - Chapter 37 看如何使用Clip修出想要的完美曲线(上)

    原文:零元学Expression Blend 4 - Chapter 37 看如何使用Clip修出想要的完美曲线(上) 几何外部的 UIElement 会在呈现的配置中以视觉化方式裁剪. 几何不一定要 ...

  10. Linux --- 程序后台运行的几种方法

    有时候我们运行一个程序,耗时比较长,所以在快下班的时候或是网络不稳定的时候就比较抓狂. 今天分享几个我在工作中用到的把程序放在后台运行的方法. nohup $ nohup --h Usage: noh ...