1.CyclicBarrier 字面意思是可循环(Cyclic)使用的屏障(Barrier)。它要做的事情是让一组线程到达一个屏障(同步点)时被阻塞,直到最后一个线程到达屏障时候,屏障才会开门。所有被屏障拦截的线程才会运行。

2.常用的方法:

  1. CyclicBarrier(int parties)
  2. 创建一个新的 CyclicBarrier,它将在给定数量的参与者(线程)处于等待状态时启动,但它不会在启动 barrier 时执行预定义的操作。
  3.  
  4. CyclicBarrier(int parties, Runnable barrierAction)
  5. 创建一个新的 CyclicBarrier,它将在给定数量的参与者(线程)处于等待状态时启动,并在启动 barrier 时执行给定的屏障操作,该操作由最后一个进入 barrier 的线程执行。
  6.  
  7. int await()
  8. 在所有参与者都已经在此 barrier 上调用 await 方法之前,将一直等待。
  9.  
  10. int await(long timeout, TimeUnit unit)
  11. 在所有参与者都已经在此屏障上调用 await 方法之前将一直等待,或者超出了指定的等待时间。
  12.  
  13. int getNumberWaiting()
  14. 返回当前在屏障处等待的参与者数目。
  15.  
  16. int getParties()
  17. 返回要求启动此 barrier 的参与者数目。
  18.  
  19. boolean isBroken()
  20. 查询此屏障是否处于损坏状态。
  21.  
  22. void reset()
  23. 将屏障重置为其初始状态。如果调用了该函数,则在等待的线程将会抛出BrokenBarrierException异常。

3.底层原理实现

CyclicBarrier是由ReentrantLock可重入锁和Condition共同实现的。

具体实现源码如下:

  1. //1.CyclicBarrier构造方法
  2. public CyclicBarrier(int parties, Runnable barrierAction) {
  3. if (parties <= 0) throw new IllegalArgumentException();
  4. // parties表示“必须同时到达barrier的线程个数”。
  5. this.parties = parties;
  6. // count表示“处在等待状态的线程个数”。
  7. this.count = parties;
  8. // barrierCommand表示“parties个线程到达barrier时,会执行的动作”。
  9. this.barrierCommand = barrierAction;
  10. }
  1. 1. await源码分析:
  1.  
  2. public int await() throws InterruptedException, BrokenBarrierException {
  3. try {
  4. return dowait(false, 0L);
  5. } catch (TimeoutException toe) {
  6. throw new Error(toe); // cannot happen;
  7. }
  8. }
  1. private int dowait(boolean timed, long nanos)
  2. throws InterruptedException, BrokenBarrierException,
  3. TimeoutException {
  4. final ReentrantLock lock = this.lock;
  5. // 获取“独占锁(lock)”
  6. lock.lock();
  7. try {
  8. // 保存“当前的generation”
  9. final Generation g = generation;
  10.  
  11. // 若“当前generation已损坏”,则抛出异常。
  12. if (g.broken)
  13. throw new BrokenBarrierException();
  14.  
  15. // 如果当前线程被中断,则通过breakBarrier()终止CyclicBarrier,唤醒CyclicBarrier中所有等待线程。
  16. if (Thread.interrupted()) {
  17. breakBarrier();
  18. throw new InterruptedException();
  19. }
  20.  
  21. // 将“count计数器”-1
  22. int index = --count;
  23. // 如果index=0,则意味着“有parties个线程到达barrier”。
  24. if (index == 0) { // tripped
  25. boolean ranAction = false;
  26. try {
  27. // 如果barrierCommand不为null,则执行该动作。
  28. final Runnable command = barrierCommand;
  29. if (command != null)
  30. command.run();
  31. ranAction = true;
  32. // 唤醒所有等待线程,并更新generation。
  33. nextGeneration();
  34. return 0;
  35. } finally {
  36. if (!ranAction)
  37. breakBarrier();
  38. }
  39. }
  40.  
  41. // 当前线程一直阻塞,直到“有parties个线程到达barrier” 或 “当前线程被中断” 或 “超时”这3者之一发生,
  42. // 当前线程才继续执行。
  43. for (;;) {
  44. try {
  45. // 如果不是“超时等待”,则调用await()进行等待;否则,调用awaitNanos()进行等待。
  46. if (!timed)
  47. trip.await();
  48. else if (nanos > 0L)
  49. nanos = trip.awaitNanos(nanos);
  50. } catch (InterruptedException ie) {
  51. // 如果等待过程中,线程被中断,则执行下面的函数。
  52. if (g == generation && ! g.broken) {
  53. breakBarrier();
  54. throw ie;
  55. } else {
  56. Thread.currentThread().interrupt();
  57. }
  58. }
  59.  
  60. // 如果“当前generation已经损坏”,则抛出异常。
  61. if (g.broken)
  62. throw new BrokenBarrierException();
  63.  
  64. // 如果“generation已经换代”,则返回index。
  65. if (g != generation)
  66. return index;
  67.  
  68. // 如果是“超时等待”,并且时间已到,则通过breakBarrier()终止CyclicBarrier,唤醒CyclicBarrier中所有等待线程,并抛出TimeoutException异常
  69. if (timed && nanos <= 0L) {
  70. breakBarrier();
  71. throw new TimeoutException();
  72. }
  73. }
  74. } finally {
  75. // 释放“独占锁(lock)”
  76. lock.unlock();
  77. }
  78. }

实例如下:

1.await()方法使用

  1. public class CyclicBarrierTest {
  2.  
  3. private static int SIZE = 5;
  4.  
  5. private static CyclicBarrier cb;
  6.  
  7. public static void main(String[] args) {
  8. cb = new CyclicBarrier(SIZE);
  9. for (int i = 0; i < SIZE; i++) {
  10. new MyTask().start();
  11. }
  12.  
  13. }
  14.  
  15. static class MyTask extends Thread {
  16. @Override
  17. public void run() {
  18. try {
  19.  
  20. System.out.println("线程" + Thread.currentThread().getName() + "正在执行同一个任务");
  21. // 以睡眠来模拟几个线程执行一个任务的时间
  22. Thread.sleep(1000);
  23. System.out.println("线程" + Thread.currentThread().getName() + "执行任务完成,等待其他线程执行完毕");
  24. // 用来挂起当前线程,直至所有线程都到达barrier状态再同时执行后续任务;
  25. cb.await();
  26.  
  27. } catch (InterruptedException e) {
  28. e.printStackTrace();
  29. } catch (BrokenBarrierException e) {
  30. e.printStackTrace();
  31. }
  32. System.out.println("所有线程写入完毕");
  33.  
  34. }
  35.  
  36. }
  37.  
  38. }

结果

  1. 线程Thread-0正在执行同一个任务
  2. 线程Thread-2正在执行同一个任务
  3. 线程Thread-1正在执行同一个任务
  4. 线程Thread-3正在执行同一个任务
  5. 线程Thread-4正在执行同一个任务
  6. 线程Thread-3执行任务完成,等待其他线程执行完毕
  7. 线程Thread-4执行任务完成,等待其他线程执行完毕
  8. 线程Thread-0执行任务完成,等待其他线程执行完毕
  9. 线程Thread-1执行任务完成,等待其他线程执行完毕
  10. 线程Thread-2执行任务完成,等待其他线程执行完毕
  11. 所有线程写入完毕
  12. 所有线程写入完毕
  13. 所有线程写入完毕
  14. 所有线程写入完毕
  15. 所有线程写入完毕

2.使用 barrier.await(2000, TimeUnit.MILLISECONDS)方法:

  1. public class CyclicBarrierWriteDataWaitTimeOutTest {
  2.  
  3. private static final int THREAD_NUM = 5;
  4.  
  5. private static final Random random = new Random();
  6.  
  7. public static void main(String[] args) throws InterruptedException {
  8.  
  9. // 使用构造方法:public CyclicBarrier(int parties, Runnable barrierAction)
  10. // 参数parties表示一共有多少线程参与这次“活动”,barrierAction是可选的,用来指定当所有线程都完成这些必须的“任务”之后要干的其他事情
  11. CyclicBarrier barrier = new CyclicBarrier(THREAD_NUM, new Runnable() {
  12.  
  13. @Override
  14. public void run() {
  15. // 最后写完数据的线程,会执行这个任务
  16. System.out.println(Thread.currentThread().getId() + ":所有线程写数据完毕!");
  17. }
  18. });
  19.  
  20. // 启动5个线程,写数据
  21. for (int i = 0; i < THREAD_NUM; i++) {
  22. if (i < THREAD_NUM - 1) {
  23. Thread t = new Thread(new MyTask(barrier));
  24. t.start();
  25. } else {
  26. // 最后一个线程延迟3秒执行
  27. Thread.sleep(3000);
  28. Thread t = new Thread(new MyTask(barrier));
  29. t.start();
  30. }
  31.  
  32. }
  33.  
  34. }
  35.  
  36. /**
  37. *
  38. * (线程类)
  39. *
  40. * <p>
  41. * 修改历史: <br>
  42. * 修改日期 修改人员 版本 修改内容<br>
  43. * -------------------------------------------------<br>
  44. * 2016年8月26日 上午11:21:39 user 1.0 初始化创建<br>
  45. * </p>
  46. *
  47. * @author Peng.Li
  48. * @version 1.0
  49. * @since JDK1.7
  50. */
  51. static class MyTask extends Thread {
  52.  
  53. private CyclicBarrier barrier;
  54.  
  55. public MyTask(CyclicBarrier barrier) {
  56. this.barrier = barrier;
  57. }
  58.  
  59. @Override
  60. public void run() {
  61.  
  62. int time = random.nextInt(1000);
  63. System.out.println(Thread.currentThread().getId() + ":需要" + time + "毫秒的时间写入数据");
  64.  
  65. try {
  66. Thread.sleep(time);
  67. } catch (InterruptedException e1) {
  68. e1.printStackTrace();
  69. }
  70. System.out.println(Thread.currentThread().getId() + ":写入数据完毕,等待其他线程写入数据");
  71. try {
  72. // 用来挂起当前线程,直至所有线程都到达barrier状态再同时执行后续任务;
  73. // 等待所有线程都调用过此函数才能继续后续动作
  74. // 只等待2s,那么最后一个线程3秒才执行,则必定会超时
  75. barrier.await(2000, TimeUnit.MILLISECONDS);
  76. } catch (InterruptedException e) {
  77. e.printStackTrace();
  78. } catch (BrokenBarrierException e) {
  79. e.printStackTrace();
  80. } catch (TimeoutException e) {
  81. e.printStackTrace();
  82. }
  83. System.out.println(Thread.currentThread().getId() + ":所有线程写入数据完毕,继续处理其他任务...");
  84.  
  85. }
  86.  
  87. }
  88.  
  89. }

从结果分析:

可以看到,前面四个线程等待最后一个线程超时了,这个时候这四个线程不会再等待最后一个线程写入完毕,而是直接抛出BrokenBarrierException

异常,继续执行后续的动作。最后一个线程完成写入数据操作后也继续了后续的动作。

需要理解的是:最后一个线程发生超时的异常,其他的线程不会继续等待,而是去执行其他的任务。

  1. Thread-1:需要650毫秒的时间写入数据
  2. Thread-3:需要297毫秒的时间写入数据
  3. Thread-5:需要755毫秒的时间写入数据
  4. Thread-7:需要79毫秒的时间写入数据
  5. Thread-7:写入数据完毕,等待其他线程写入数据
  6. Thread-3:写入数据完毕,等待其他线程写入数据
  7. Thread-1:写入数据完毕,等待其他线程写入数据
  8. Thread-5:写入数据完毕,等待其他线程写入数据
  9. Thread-7:所有线程写入数据完毕,继续处理其他任务...
  10. Thread-1:所有线程写入数据完毕,继续处理其他任务...
  11. java.util.concurrent.TimeoutException
  12. Thread-3:所有线程写入数据完毕,继续处理其他任务...
  13. Thread-5:所有线程写入数据完毕,继续处理其他任务...
  14. at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
  15. at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:427)
  16. at concurrentMy.CountAndCyclicAndSemaphore.cyclibarrier.test.CyclicBarrierWriteDataWaitTimeOutTest$MyTask.run(CyclicBarrierWriteDataWaitTimeOutTest.java:299)
  17. at java.lang.Thread.run(Thread.java:745)
  18. java.util.concurrent.BrokenBarrierException
  19. at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:243)
  20. at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:427)
  21. at concurrentMy.CountAndCyclicAndSemaphore.cyclibarrier.test.CyclicBarrierWriteDataWaitTimeOutTest$MyTask.run(CyclicBarrierWriteDataWaitTimeOutTest.java:299)
  22. at java.lang.Thread.run(Thread.java:745)
  23. java.util.concurrent.BrokenBarrierException
  24. at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:243)
  25. at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:427)
  26. at concurrentMy.CountAndCyclicAndSemaphore.cyclibarrier.test.CyclicBarrierWriteDataWaitTimeOutTest$MyTask.run(CyclicBarrierWriteDataWaitTimeOutTest.java:299)
  27. at java.lang.Thread.run(Thread.java:745)
  28. java.util.concurrent.BrokenBarrierException
  29. at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:243)
  30. at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:427)
  31. at concurrentMy.CountAndCyclicAndSemaphore.cyclibarrier.test.CyclicBarrierWriteDataWaitTimeOutTest$MyTask.run(CyclicBarrierWriteDataWaitTimeOutTest.java:299)
  32. at java.lang.Thread.run(Thread.java:745)
  33. Thread-9:需要164毫秒的时间写入数据
  34. Thread-9:写入数据完毕,等待其他线程写入数据java.util.concurrent.BrokenBarrierException
  35. Thread-9:所有线程写入数据完毕,继续处理其他任务...
  36.  
  37. at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:200)
  38. at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:427)
  39. at concurrentMy.CountAndCyclicAndSemaphore.cyclibarrier.test.CyclicBarrierWriteDataWaitTimeOutTest$MyTask.run(CyclicBarrierWriteDataWaitTimeOutTest.java:299)
  40. at java.lang.Thread.run(Thread.java:745)

JUC回顾之-CyclicBarrier底层实现和原理的更多相关文章

  1. JUC回顾之-ArrayBlockingQueue底层实现和原理

    ArrayBlockingQueue的原理和底层实现的数据结构 : ArrayBlockingQueue是数组实现的线程安全的有界的阻塞队列,可以按照 FIFO(先进先出)原则对元素进行排序. 线程安 ...

  2. JUC回顾之-Semaphore底层实现和原理

    1.控制并发线程数的Semaphore Semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,保证合理的使用公共资源. 线程可以通过acquire()方法来获取信号量的 ...

  3. JUC回顾之-ScheduledThreadPoolExecutor底层实现原理和应用

    项目中经常使用定时器,比如每隔一段时间清理下线过期的F码,或者应用timer定期查询MQ在数据库的配置,根据不同version实现配置的实时更新等等.但是timer是存在一些缺陷的,因为Timer在执 ...

  4. JUC回顾之-AQS同步器的实现原理

    1.什么是AQS? AQS的核心思想是基于volatile int state这样的volatile变量,配合Unsafe工具对其原子性的操作来实现对当前锁状态进行修改.同步器内部依赖一个FIFO的双 ...

  5. JUC中的AQS底层详细超详解

    摘要:当你使用java实现一个线程同步的对象时,一定会包含一个问题:你该如何保证多个线程访问该对象时,正确地进行阻塞等待,正确地被唤醒? 本文分享自华为云社区<JUC中的AQS底层详细超详解,剖 ...

  6. Java多线程系列--“JUC线程池”03之 线程池原理(二)

    概要 在前面一章"Java多线程系列--“JUC线程池”02之 线程池原理(一)"中介绍了线程池的数据结构,本章会通过分析线程池的源码,对线程池进行说明.内容包括:线程池示例参考代 ...

  7. Java多线程系列--“JUC线程池”04之 线程池原理(三)

    转载请注明出处:http://www.cnblogs.com/skywang12345/p/3509960.html 本章介绍线程池的生命周期.在"Java多线程系列--“基础篇”01之 基 ...

  8. Java多线程系列--“JUC线程池”05之 线程池原理(四)

    概要 本章介绍线程池的拒绝策略.内容包括:拒绝策略介绍拒绝策略对比和示例 转载请注明出处:http://www.cnblogs.com/skywang12345/p/3512947.html 拒绝策略 ...

  9. 从底层谈WebGIS 原理设计与实现(三):WebGIS前端地图显示之根据地理范围换算出瓦片行列号的原理(转载)

    从底层谈WebGIS 原理设计与实现(三):WebGIS前端地图显示之根据地理范围换算出瓦片行列号的原理 1.前言   在上一节中我们知道了屏幕上一像素等于实际中多少单位长度(米或经纬度)的换算方法, ...

随机推荐

  1. 极大似然估计、贝叶斯估计、EM算法

    参考文献:http://blog.csdn.net/zouxy09/article/details/8537620 极大似然估计 已知样本满足某种概率分布,但是其中具体的参数不清楚,极大似然估计估计就 ...

  2. serialVersionUID要注意以下几点:

    今天在使用eclipse开发的时候,遇到一个warning,看到warning我总觉得不爽,使用自动修复后,发现eclipse在代码中加入了“private static final long ser ...

  3. Mongodb For C# "Query" 对象常用的方法

    Query.All("name", "a", "b");//通过多个元素来匹配数组 Query.In("name", & ...

  4. java本地方法如何调用其他程序函数,方法详解

    JNI是Java Native Interface的缩写,中文为JAVA本地调用.从Java 1.1 开始,Java Native Interface (JNI)标准成为java平台的一部分,它允许J ...

  5. webexam项目杂记

    sql 语句 数据库 本身 有数据类型的区分,对于mysql的字符串默认的用单引号''来表示,因此,整个sql 语句就要用双引号来括. 如: $sql = "SELECT * FROM us ...

  6. ExtJS学习之路第四步:看源码,实战MessageBox

    可以通过看MessageBox.js的源码来深入认识,记住它的主要用法.Ext.MessageBox是实用类,用于生成不同风格的消息框,它是Singleton(单例),别名Ext.Msg.注意Mess ...

  7. 错误 1 未知的服务器标记“asp:ScriptManager”。

    如题 ... 解决方案 :将 <%@ Register Assembly="System.Web.Extensions, Version=1.0.61025.0, Culture=ne ...

  8. HDOJ 1083 Courses

    Hopcroft-Karp算法模板 Courses Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 65536/32768 K (J ...

  9. Bridge 使用

  10. Vector3.Lerp 插值

    Vector3.Lerp 插值 static function Lerp (from : Vector3, to : Vector3, t : float) : Vector3 Description ...