这是java高并发系列第17篇。

本文主要内容:

  1. 介绍CyclicBarrier
  2. 6个示例介绍CyclicBarrier的使用
  3. 对比CyclicBarrier和CountDownLatch

CyclicBarrier简介

CyclicBarrier通常称为循环屏障。它和CountDownLatch很相似,都可以使线程先等待然后再执行。不过CountDownLatch是使一批线程等待另一批线程执行完后再执行;而CyclicBarrier只是使等待的线程达到一定数目后再让它们继续执行。故而CyclicBarrier内部也有一个计数器,计数器的初始值在创建对象时通过构造参数指定,如下所示:

  1. public CyclicBarrier(int parties) {
  2. this(parties, null);
  3. }

每调用一次await()方法都将使阻塞的线程数+1,只有阻塞的线程数达到设定值时屏障才会打开,允许阻塞的所有线程继续执行。除此之外,CyclicBarrier还有几点需要注意的地方:

  • CyclicBarrier的计数器可以重置而CountDownLatch不行,这意味着CyclicBarrier实例可以被重复使用而CountDownLatch只能被使用一次。而这也是循环屏障循环二字的语义所在。
  • CyclicBarrier允许用户自定义barrierAction操作,这是个可选操作,可以在创建CyclicBarrier对象时指定
  1. public CyclicBarrier(int parties, Runnable barrierAction) {
  2. if (parties <= 0) throw new IllegalArgumentException();
  3. this.parties = parties;
  4. this.count = parties;
  5. this.barrierCommand = barrierAction;
  6. }

一旦用户在创建CyclicBarrier对象时设置了barrierAction参数,则在阻塞线程数达到设定值屏障打开前,会调用barrierAction的run()方法完成用户自定义的操作。

示例1:简单使用CyclicBarrier

公司组织旅游,大家都有经历过,10个人,中午到饭点了,需要等到10个人都到了才能开饭,先到的人坐那等着,代码如下:

  1. package com.itsoku.chat15;
  2. import java.util.concurrent.BrokenBarrierException;
  3. import java.util.concurrent.CyclicBarrier;
  4. import java.util.concurrent.TimeUnit;
  5. /**
  6. * 微信公众号:javacode2018,获取年薪50万java课程
  7. */
  8. public class Demo1 {
  9. public static CyclicBarrier cyclicBarrier = new CyclicBarrier(10);
  10. public static class T extends Thread {
  11. int sleep;
  12. public T(String name, int sleep) {
  13. super(name);
  14. this.sleep = sleep;
  15. }
  16. @Override
  17. public void run() {
  18. try {
  19. //模拟休眠
  20. TimeUnit.SECONDS.sleep(sleep);
  21. long starTime = System.currentTimeMillis();
  22. //调用await()的时候,当前线程将会被阻塞,需要等待其他员工都到达await了才能继续
  23. cyclicBarrier.await();
  24. long endTime = System.currentTimeMillis();
  25. System.out.println(this.getName() + ",sleep:" + this.sleep + " 等待了" + (endTime - starTime) + "(ms),开始吃饭了!");
  26. } catch (InterruptedException e) {
  27. e.printStackTrace();
  28. } catch (BrokenBarrierException e) {
  29. e.printStackTrace();
  30. }
  31. }
  32. }
  33. public static void main(String[] args) throws InterruptedException {
  34. for (int i = 1; i <= 10; i++) {
  35. new T("员工" + i, i).start();
  36. }
  37. }
  38. }

输出:

  1. 员工1,sleep:1 等待了9000(ms),开始吃饭了!
  2. 员工9,sleep:9 等待了1000(ms),开始吃饭了!
  3. 员工8,sleep:8 等待了2001(ms),开始吃饭了!
  4. 员工7,sleep:7 等待了3001(ms),开始吃饭了!
  5. 员工6,sleep:6 等待了4001(ms),开始吃饭了!
  6. 员工4,sleep:4 等待了6000(ms),开始吃饭了!
  7. 员工5,sleep:5 等待了5000(ms),开始吃饭了!
  8. 员工10,sleep:10 等待了0(ms),开始吃饭了!
  9. 员工2,sleep:2 等待了7999(ms),开始吃饭了!
  10. 员工3,sleep:3 等待了7000(ms),开始吃饭了!

代码中模拟了10个员工上桌吃饭的场景,等待所有员工都到齐了才能开发,可以看到第10个员工最慢,前面的都在等待第10个员工,员工1等待了9秒,上面代码中调用cyclicBarrier.await();会让当前线程等待。当10个员工都调用了cyclicBarrier.await();之后,所有处于等待中的员工都会被唤醒,然后继续运行。

示例2:重复使用CyclicBarrier

对示例1进行改造一下,吃饭完毕之后,所有人都去车上,待所有人都到车上之后,驱车去下一景点玩。

  1. package com.itsoku.chat15;
  2. import java.util.concurrent.BrokenBarrierException;
  3. import java.util.concurrent.CyclicBarrier;
  4. import java.util.concurrent.TimeUnit;
  5. /**
  6. * 微信公众号:javacode2018,获取年薪50万java课程
  7. */
  8. public class Demo2 {
  9. public static CyclicBarrier cyclicBarrier = new CyclicBarrier(10);
  10. public static class T extends Thread {
  11. int sleep;
  12. public T(String name, int sleep) {
  13. super(name);
  14. this.sleep = sleep;
  15. }
  16. //等待吃饭
  17. void eat() {
  18. try {
  19. //模拟休眠
  20. TimeUnit.SECONDS.sleep(sleep);
  21. long starTime = System.currentTimeMillis();
  22. //调用await()的时候,当前线程将会被阻塞,需要等待其他员工都到达await了才能继续
  23. cyclicBarrier.await();
  24. long endTime = System.currentTimeMillis();
  25. System.out.println(this.getName() + ",sleep:" + this.sleep + " 等待了" + (endTime - starTime) + "(ms),开始吃饭了!");
  26. //休眠sleep时间,模拟当前员工吃饭耗时
  27. TimeUnit.SECONDS.sleep(sleep);
  28. } catch (InterruptedException e) {
  29. e.printStackTrace();
  30. } catch (BrokenBarrierException e) {
  31. e.printStackTrace();
  32. }
  33. }
  34. //等待所有人到齐之后,开车去下一站
  35. void drive() {
  36. try {
  37. long starTime = System.currentTimeMillis();
  38. //调用await()的时候,当前线程将会被阻塞,需要等待其他员工都到达await了才能继续
  39. cyclicBarrier.await();
  40. long endTime = System.currentTimeMillis();
  41. System.out.println(this.getName() + ",sleep:" + this.sleep + " 等待了" + (endTime - starTime) + "(ms),去下一景点的路上!");
  42. } catch (InterruptedException e) {
  43. e.printStackTrace();
  44. } catch (BrokenBarrierException e) {
  45. e.printStackTrace();
  46. }
  47. }
  48. @Override
  49. public void run() {
  50. //等待所有人到齐之后吃饭,先到的人坐那等着,什么事情不要干
  51. this.eat();
  52. //等待所有人到齐之后开车去下一景点,先到的人坐那等着,什么事情不要干
  53. this.drive();
  54. }
  55. }
  56. public static void main(String[] args) throws InterruptedException {
  57. for (int i = 1; i <= 10; i++) {
  58. new T("员工" + i, i).start();
  59. }
  60. }
  61. }

输出:

  1. 员工10,sleep:10 等待了0(ms),开始吃饭了!
  2. 员工5,sleep:5 等待了5000(ms),开始吃饭了!
  3. 员工6,sleep:6 等待了4000(ms),开始吃饭了!
  4. 员工9,sleep:9 等待了1001(ms),开始吃饭了!
  5. 员工4,sleep:4 等待了6000(ms),开始吃饭了!
  6. 员工3,sleep:3 等待了7000(ms),开始吃饭了!
  7. 员工1,sleep:1 等待了9001(ms),开始吃饭了!
  8. 员工2,sleep:2 等待了8000(ms),开始吃饭了!
  9. 员工8,sleep:8 等待了2001(ms),开始吃饭了!
  10. 员工7,sleep:7 等待了3000(ms),开始吃饭了!
  11. 员工10,sleep:10 等待了0(ms),去下一景点的路上!
  12. 员工1,sleep:1 等待了8998(ms),去下一景点的路上!
  13. 员工5,sleep:5 等待了4999(ms),去下一景点的路上!
  14. 员工4,sleep:4 等待了5999(ms),去下一景点的路上!
  15. 员工3,sleep:3 等待了6998(ms),去下一景点的路上!
  16. 员工2,sleep:2 等待了7998(ms),去下一景点的路上!
  17. 员工9,sleep:9 等待了999(ms),去下一景点的路上!
  18. 员工8,sleep:8 等待了1999(ms),去下一景点的路上!
  19. 员工7,sleep:7 等待了2999(ms),去下一景点的路上!
  20. 员工6,sleep:6 等待了3999(ms),去下一景点的路上!

坑,又是员工10最慢,要提升效率了,不能吃的太多,得减肥。

代码中CyclicBarrier相当于使用了2次,第一次用于等待所有人到达后开饭,第二次用于等待所有人上车后驱车去下一景点。注意一些先到的员工会在其他人到达之前,都处于等待状态(cyclicBarrier.await();会让当前线程阻塞),无法干其他事情,等到最后一个人到了会唤醒所有人,然后继续。

CyclicBarrier内部相当于有个计数器(构造方法传入的),每次调用await();后,计数器会减1,并且await()方法会让当前线程阻塞,等待计数器减为0的时候,所有在await()上等待的线程被唤醒,然后继续向下执行,此时计数器又会被还原为创建时的值,然后可以继续再次使用。

示例3:最后到的人给大家上酒,然后开饭

还是示例1中的例子,员工10是最后到达的,让所有人都久等了,那怎么办,得给所有人倒酒,然后开饭,代码如下:

  1. package com.itsoku.chat15;
  2. import java.util.concurrent.BrokenBarrierException;
  3. import java.util.concurrent.CyclicBarrier;
  4. import java.util.concurrent.TimeUnit;
  5. /**
  6. * 微信公众号:javacode2018,获取年薪50万java课程
  7. */
  8. public class Demo3 {
  9. public static CyclicBarrier cyclicBarrier = new CyclicBarrier(10, () -> {
  10. //模拟倒酒,花了2秒,又得让其他9个人等2秒
  11. try {
  12. TimeUnit.SECONDS.sleep(2);
  13. } catch (InterruptedException e) {
  14. e.printStackTrace();
  15. }
  16. System.out.println(Thread.currentThread().getName() + "说,不好意思,让大家久等了,给大家倒酒赔罪!");
  17. });
  18. public static class T extends Thread {
  19. int sleep;
  20. public T(String name, int sleep) {
  21. super(name);
  22. this.sleep = sleep;
  23. }
  24. @Override
  25. public void run() {
  26. try {
  27. //模拟休眠
  28. TimeUnit.SECONDS.sleep(sleep);
  29. long starTime = System.currentTimeMillis();
  30. //调用await()的时候,当前线程将会被阻塞,需要等待其他员工都到达await了才能继续
  31. cyclicBarrier.await();
  32. long endTime = System.currentTimeMillis();
  33. System.out.println(this.getName() + ",sleep:" + this.sleep + " 等待了" + (endTime - starTime) + "(ms),开始吃饭了!");
  34. } catch (InterruptedException e) {
  35. e.printStackTrace();
  36. } catch (BrokenBarrierException e) {
  37. e.printStackTrace();
  38. }
  39. }
  40. }
  41. public static void main(String[] args) throws InterruptedException {
  42. for (int i = 1; i <= 10; i++) {
  43. new T("员工" + i, i).start();
  44. }
  45. }
  46. }

输出:

  1. 员工10说,不好意思,让大家久等了,给大家倒酒赔罪!
  2. 员工10,sleep:10 等待了2000(ms),开始吃饭了!
  3. 员工1,sleep:1 等待了11000(ms),开始吃饭了!
  4. 员工2,sleep:2 等待了10000(ms),开始吃饭了!
  5. 员工5,sleep:5 等待了7000(ms),开始吃饭了!
  6. 员工7,sleep:7 等待了5000(ms),开始吃饭了!
  7. 员工9,sleep:9 等待了3000(ms),开始吃饭了!
  8. 员工4,sleep:4 等待了8000(ms),开始吃饭了!
  9. 员工3,sleep:3 等待了9001(ms),开始吃饭了!
  10. 员工8,sleep:8 等待了4001(ms),开始吃饭了!
  11. 员工6,sleep:6 等待了6001(ms),开始吃饭了!

代码中创建CyclicBarrier对象时,多传入了一个参数(内部是倒酒操作),先到的人先等待,待所有人都到齐之后,需要先给大家倒酒,然后唤醒所有等待中的人让大家开饭。从输出结果中我们发现,倒酒操作是由最后一个人操作的,最后一个人倒酒完毕之后,才唤醒所有等待中的其他员工,让大家开饭。

示例4:其中一个人等待中被打断了

员工5等待中,突然接了个电话,有点急事,然后就拿起筷子开吃了,其他人会怎么样呢?看着他吃么?

代码如下:

  1. package com.itsoku.chat15;
  2. import java.sql.Time;
  3. import java.util.concurrent.BrokenBarrierException;
  4. import java.util.concurrent.CyclicBarrier;
  5. import java.util.concurrent.TimeUnit;
  6. /**
  7. * 微信公众号:javacode2018,获取年薪50万java课程
  8. */
  9. public class Demo4 {
  10. public static CyclicBarrier cyclicBarrier = new CyclicBarrier(10);
  11. public static class T extends Thread {
  12. int sleep;
  13. public T(String name, int sleep) {
  14. super(name);
  15. this.sleep = sleep;
  16. }
  17. @Override
  18. public void run() {
  19. long starTime = 0, endTime = 0;
  20. try {
  21. //模拟休眠
  22. TimeUnit.SECONDS.sleep(sleep);
  23. starTime = System.currentTimeMillis();
  24. //调用await()的时候,当前线程将会被阻塞,需要等待其他员工都到达await了才能继续
  25. System.out.println(this.getName() + "到了!");
  26. cyclicBarrier.await();
  27. } catch (InterruptedException e) {
  28. e.printStackTrace();
  29. } catch (BrokenBarrierException e) {
  30. e.printStackTrace();
  31. }
  32. endTime = System.currentTimeMillis();
  33. System.out.println(this.getName() + ",sleep:" + this.sleep + " 等待了" + (endTime - starTime) + "(ms),开始吃饭了!");
  34. }
  35. }
  36. public static void main(String[] args) throws InterruptedException {
  37. for (int i = 1; i <= 10; i++) {
  38. int sleep = 0;
  39. if (i == 10) {
  40. sleep = 10;
  41. }
  42. T t = new T("员工" + i, sleep);
  43. t.start();
  44. if (i == 5) {
  45. //模拟员工5接了个电话,将自己等待吃饭给打断了
  46. TimeUnit.SECONDS.sleep(1);
  47. System.out.println(t.getName() + ",有点急事,我先开干了!");
  48. t.interrupt();
  49. TimeUnit.SECONDS.sleep(2);
  50. }
  51. }
  52. }
  53. }

输出:

  1. 员工4到了!
  2. 员工3到了!
  3. 员工5到了!
  4. 员工1到了!
  5. 员工2到了!
  6. 员工5,有点急事,我先开干了!
  7. java.util.concurrent.BrokenBarrierException
  8. 员工1,sleep:0 等待了1001(ms),开始吃饭了!
  9. at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
  10. 员工3,sleep:0 等待了1001(ms),开始吃饭了!
  11. at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
  12. 员工4,sleep:0 等待了1001(ms),开始吃饭了!
  13. at com.itsoku.chat15.Demo4$T.run(Demo4.java:31)
  14. 员工2,sleep:0 等待了1001(ms),开始吃饭了!
  15. 员工5,sleep:0 等待了1002(ms),开始吃饭了!
  16. java.util.concurrent.BrokenBarrierException
  17. at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
  18. at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
  19. at com.itsoku.chat15.Demo4$T.run(Demo4.java:31)
  20. java.util.concurrent.BrokenBarrierException
  21. at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
  22. at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
  23. at com.itsoku.chat15.Demo4$T.run(Demo4.java:31)
  24. java.util.concurrent.BrokenBarrierException
  25. at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
  26. at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
  27. at com.itsoku.chat15.Demo4$T.run(Demo4.java:31)
  28. java.lang.InterruptedException
  29. at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.reportInterruptAfterWait(AbstractQueuedSynchronizer.java:2014)
  30. at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2048)
  31. at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:234)
  32. at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
  33. at com.itsoku.chat15.Demo4$T.run(Demo4.java:31)
  34. java.util.concurrent.BrokenBarrierException
  35. at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)
  36. at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
  37. at com.itsoku.chat15.Demo4$T.run(Demo4.java:31)
  38. java.util.concurrent.BrokenBarrierException
  39. 员工6到了!
  40. at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)
  41. at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
  42. 员工9到了!
  43. at com.itsoku.chat15.Demo4$T.run(Demo4.java:31)
  44. 员工8到了!
  45. 员工7到了!
  46. 员工6,sleep:0 等待了0(ms),开始吃饭了!
  47. 员工7,sleep:0 等待了1(ms),开始吃饭了!
  48. java.util.concurrent.BrokenBarrierException
  49. at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)
  50. at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
  51. at com.itsoku.chat15.Demo4$T.run(Demo4.java:31)
  52. java.util.concurrent.BrokenBarrierException
  53. at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)
  54. at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
  55. at com.itsoku.chat15.Demo4$T.run(Demo4.java:31)
  56. 员工8,sleep:0 等待了1(ms),开始吃饭了!
  57. 员工9,sleep:0 等待了1(ms),开始吃饭了!
  58. Disconnected from the target VM, address: '127.0.0.1:64413', transport: 'socket'
  59. java.util.concurrent.BrokenBarrierException
  60. at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)
  61. at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
  62. at com.itsoku.chat15.Demo4$T.run(Demo4.java:31)
  63. 员工10到了!
  64. 员工10,sleep:10 等待了0(ms),开始吃饭了!

输出的信息看着有点乱,给大家理一理,员工5遇到急事,拿起筷子就是吃,这样好么,当然不好,他这么做了,后面看他这么做了都跟着这么做(这种场景是不是很熟悉,有一个人拿起筷子先吃起来,其他人都跟着上了),直接不等其他人了,拿起筷子就开吃了。CyclicBarrier遇到这种情况就是这么处理的。前面4个员工都在await()处等待着,员工5也在await()上等待着,等了1秒(TimeUnit.SECONDS.sleep(1);),接了个电话,然后给员工5发送中断信号后(t.interrupt();),员工5的await()方法会触发InterruptedException异常,此时其他等待中的前4个员工,看着5开吃了,自己立即也不等了,内部从await()方法中触发BrokenBarrierException异常,然后也开吃了,后面的6/7/8/9/10员工来了以后发现大家都开吃了,自己也不等了,6-10员工调用await()直接抛出了BrokenBarrierException异常,然后继续向下。

结论:

  1. 内部有一个人把规则破坏了(接收到中断信号),其他人都不按规则来了,不会等待了
  2. 接收到中断信号的线程,await方法会触发InterruptedException异常,然后被唤醒向下运行
  3. 其他等待中 或者后面到达的线程,会在await()方法上触发BrokenBarrierException异常,然后继续执行

示例5:其中一个人只愿意等待5秒

基于示例1,员工1只愿意等的5秒,5s后如果大家还没到期,自己要开吃了,员工1开吃了,其他人会怎么样呢?

  1. package com.itsoku.chat15;
  2. import java.util.concurrent.BrokenBarrierException;
  3. import java.util.concurrent.CyclicBarrier;
  4. import java.util.concurrent.TimeUnit;
  5. import java.util.concurrent.TimeoutException;
  6. /**
  7. * 微信公众号:javacode2018,获取年薪50万java课程
  8. */
  9. public class Demo5 {
  10. public static CyclicBarrier cyclicBarrier = new CyclicBarrier(10);
  11. public static class T extends Thread {
  12. int sleep;
  13. public T(String name, int sleep) {
  14. super(name);
  15. this.sleep = sleep;
  16. }
  17. @Override
  18. public void run() {
  19. long starTime = 0, endTime = 0;
  20. try {
  21. //模拟休眠
  22. TimeUnit.SECONDS.sleep(sleep);
  23. starTime = System.currentTimeMillis();
  24. //调用await()的时候,当前线程将会被阻塞,需要等待其他员工都到达await了才能继续
  25. System.out.println(this.getName() + "到了!");
  26. if (this.getName().equals("员工1")) {
  27. cyclicBarrier.await(5, TimeUnit.SECONDS);
  28. } else {
  29. cyclicBarrier.await();
  30. }
  31. } catch (InterruptedException e) {
  32. e.printStackTrace();
  33. } catch (BrokenBarrierException e) {
  34. e.printStackTrace();
  35. } catch (TimeoutException e) {
  36. e.printStackTrace();
  37. }
  38. endTime = System.currentTimeMillis();
  39. System.out.println(this.getName() + ",sleep:" + this.sleep + " 等待了" + (endTime - starTime) + "(ms),开始吃饭了!");
  40. }
  41. }
  42. public static void main(String[] args) throws InterruptedException {
  43. for (int i = 1; i <= 10; i++) {
  44. T t = new T("员工" + i, i);
  45. t.start();
  46. }
  47. }
  48. }

输出:

  1. 员工1到了!
  2. 员工2到了!
  3. 员工3到了!
  4. 员工4到了!
  5. 员工5到了!
  6. 员工6到了!
  7. 员工1,sleep:1 等待了5001(ms),开始吃饭了!
  8. 员工5,sleep:5 等待了1001(ms),开始吃饭了!
  9. java.util.concurrent.TimeoutException
  10. at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:257)
  11. at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:435)
  12. at com.itsoku.chat15.Demo5$T.run(Demo5.java:32)
  13. java.util.concurrent.BrokenBarrierException
  14. at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
  15. at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
  16. at com.itsoku.chat15.Demo5$T.run(Demo5.java:34)
  17. java.util.concurrent.BrokenBarrierException
  18. at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)
  19. at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
  20. at com.itsoku.chat15.Demo5$T.run(Demo5.java:34)
  21. 员工6,sleep:6 等待了2(ms),开始吃饭了!
  22. java.util.concurrent.BrokenBarrierException
  23. 员工2,sleep:2 等待了4002(ms),开始吃饭了!
  24. at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
  25. 员工3,sleep:3 等待了3001(ms),开始吃饭了!
  26. at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
  27. 员工4,sleep:4 等待了2001(ms),开始吃饭了!
  28. at com.itsoku.chat15.Demo5$T.run(Demo5.java:34)
  29. java.util.concurrent.BrokenBarrierException
  30. at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
  31. at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
  32. at com.itsoku.chat15.Demo5$T.run(Demo5.java:34)
  33. java.util.concurrent.BrokenBarrierException
  34. at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
  35. at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
  36. at com.itsoku.chat15.Demo5$T.run(Demo5.java:34)
  37. java.util.concurrent.BrokenBarrierException
  38. 员工7到了!
  39. at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)
  40. 员工7,sleep:7 等待了0(ms),开始吃饭了!
  41. at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
  42. at com.itsoku.chat15.Demo5$T.run(Demo5.java:34)
  43. 员工8到了!
  44. 员工8,sleep:8 等待了0(ms),开始吃饭了!
  45. java.util.concurrent.BrokenBarrierException
  46. at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)
  47. at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
  48. at com.itsoku.chat15.Demo5$T.run(Demo5.java:34)
  49. 员工9到了!
  50. java.util.concurrent.BrokenBarrierException
  51. 员工9,sleep:9 等待了0(ms),开始吃饭了!
  52. at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)
  53. at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
  54. at com.itsoku.chat15.Demo5$T.run(Demo5.java:34)
  55. java.util.concurrent.BrokenBarrierException
  56. 员工10到了!
  57. at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)
  58. at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
  59. 员工10,sleep:10 等待了0(ms),开始吃饭了!
  60. at com.itsoku.chat15.Demo5$T.run(Demo5.java:34)

从输出结果中我们可以看到:1等待5秒之后,开吃了,其他等待人都开吃了,后面来的人不等待,直接开吃了。

员工1调用有参await方法等待5秒之后,触发了TimeoutException异常,然后继续向下运行,其他的在5开吃之前已经等了一会的的几个员工,他们看到5开吃了,自己立即不等待了,也开吃了(他们的await抛出了BrokenBarrierException异常);还有几个员工在5开吃之后到达的,他们直接不等待了,直接抛出BrokenBarrierException异常,然后也开吃了。

结论:

  1. 等待超时的方法
    1. public int await(long timeout, TimeUnit unit) throws InterruptedException,BrokenBarrierException,TimeoutException
  2. 内部有一个人把规则破坏了(等待超时),其他人都不按规则来了,不会等待了
  3. 等待超时的线程,await方法会触发TimeoutException异常,然后被唤醒向下运行
  4. 其他等待中或者后面到达的线程,会在await()方法上触发BrokenBarrierException异常,然后继续执行

示例6:重建规则

示例5中改造一下,员工1等待5秒超时之后,开吃了,打破了规则,先前等待中的以及后面到达的都不按规则来了,都拿起筷子开吃。过了一会,导游重新告知大家,要按规则来,然后重建了规则,大家都按规则来了。

代码如下:

  1. package com.itsoku.chat15;
  2. import java.util.concurrent.BrokenBarrierException;
  3. import java.util.concurrent.CyclicBarrier;
  4. import java.util.concurrent.TimeUnit;
  5. import java.util.concurrent.TimeoutException;
  6. /**
  7. * 微信公众号:javacode2018,获取年薪50万java课程
  8. */
  9. public class Demo6 {
  10. public static CyclicBarrier cyclicBarrier = new CyclicBarrier(10);
  11. //规则是否已重建
  12. public static boolean guizhe = false;
  13. public static class T extends Thread {
  14. int sleep;
  15. public T(String name, int sleep) {
  16. super(name);
  17. this.sleep = sleep;
  18. }
  19. @Override
  20. public void run() {
  21. long starTime = 0, endTime = 0;
  22. try {
  23. //模拟休眠
  24. TimeUnit.SECONDS.sleep(sleep);
  25. starTime = System.currentTimeMillis();
  26. //调用await()的时候,当前线程将会被阻塞,需要等待其他员工都到达await了才能继续
  27. System.out.println(this.getName() + "到了!");
  28. if (!guizhe) {
  29. if (this.getName().equals("员工1")) {
  30. cyclicBarrier.await(5, TimeUnit.SECONDS);
  31. } else {
  32. cyclicBarrier.await();
  33. }
  34. } else {
  35. cyclicBarrier.await();
  36. }
  37. } catch (InterruptedException e) {
  38. e.printStackTrace();
  39. } catch (BrokenBarrierException e) {
  40. e.printStackTrace();
  41. } catch (TimeoutException e) {
  42. e.printStackTrace();
  43. }
  44. endTime = System.currentTimeMillis();
  45. System.out.println(this.getName() + ",sleep:" + this.sleep + " 等待了" + (endTime - starTime) + "(ms),开始吃饭了!");
  46. }
  47. }
  48. public static void main(String[] args) throws InterruptedException {
  49. for (int i = 1; i <= 10; i++) {
  50. T t = new T("员工" + i, i);
  51. t.start();
  52. }
  53. //等待10秒之后,重置,重建规则
  54. TimeUnit.SECONDS.sleep(15);
  55. cyclicBarrier.reset();
  56. guizhe = true;
  57. System.out.println("---------------大家太皮了,请大家按规则来------------------");
  58. //再来一次
  59. for (int i = 1; i <= 10; i++) {
  60. T t = new T("员工" + i, i);
  61. t.start();
  62. }
  63. }
  64. }

输出:

  1. 员工1到了!
  2. 员工2到了!
  3. 员工3到了!
  4. 员工4到了!
  5. 员工5到了!
  6. java.util.concurrent.TimeoutException
  7. at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:257)
  8. at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:435)
  9. at com.itsoku.chat15.Demo6$T.run(Demo6.java:36)
  10. java.util.concurrent.BrokenBarrierException
  11. at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
  12. at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
  13. at com.itsoku.chat15.Demo6$T.run(Demo6.java:38)
  14. java.util.concurrent.BrokenBarrierException
  15. at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
  16. at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
  17. at com.itsoku.chat15.Demo6$T.run(Demo6.java:38)
  18. java.util.concurrent.BrokenBarrierException
  19. at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
  20. at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
  21. at com.itsoku.chat15.Demo6$T.run(Demo6.java:38)
  22. java.util.concurrent.BrokenBarrierException
  23. at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
  24. at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
  25. at com.itsoku.chat15.Demo6$T.run(Demo6.java:38)
  26. java.util.concurrent.BrokenBarrierException
  27. at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
  28. at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
  29. at com.itsoku.chat15.Demo6$T.run(Demo6.java:38)
  30. 员工6到了!
  31. 员工1,sleep:1 等待了5002(ms),开始吃饭了!
  32. 员工6,sleep:6 等待了4(ms),开始吃饭了!
  33. 员工4,sleep:4 等待了2004(ms),开始吃饭了!
  34. 员工5,sleep:5 等待了1004(ms),开始吃饭了!
  35. 员工3,sleep:3 等待了3002(ms),开始吃饭了!
  36. 员工2,sleep:2 等待了4004(ms),开始吃饭了!
  37. 员工7到了!
  38. 员工7,sleep:7 等待了0(ms),开始吃饭了!
  39. java.util.concurrent.BrokenBarrierException
  40. at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)
  41. at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
  42. at com.itsoku.chat15.Demo6$T.run(Demo6.java:38)
  43. java.util.concurrent.BrokenBarrierException
  44. at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)
  45. at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
  46. at com.itsoku.chat15.Demo6$T.run(Demo6.java:38)
  47. 员工8到了!
  48. 员工8,sleep:8 等待了0(ms),开始吃饭了!
  49. java.util.concurrent.BrokenBarrierException
  50. 员工9到了!
  51. at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)
  52. 员工9,sleep:9 等待了0(ms),开始吃饭了!
  53. at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
  54. at com.itsoku.chat15.Demo6$T.run(Demo6.java:38)
  55. java.util.concurrent.BrokenBarrierException
  56. 员工10到了!
  57. at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)
  58. 员工10,sleep:10 等待了0(ms),开始吃饭了!
  59. at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:362)
  60. at com.itsoku.chat15.Demo6$T.run(Demo6.java:38)
  61. ---------------大家太皮了,请大家按规则来------------------
  62. 员工1到了!
  63. 员工2到了!
  64. 员工3到了!
  65. 员工4到了!
  66. 员工5到了!
  67. 员工6到了!
  68. 员工7到了!
  69. 员工8到了!
  70. 员工9到了!
  71. 员工10到了!
  72. 员工10,sleep:10 等待了0(ms),开始吃饭了!
  73. 员工1,sleep:1 等待了9000(ms),开始吃饭了!
  74. 员工2,sleep:2 等待了8000(ms),开始吃饭了!
  75. 员工3,sleep:3 等待了6999(ms),开始吃饭了!
  76. 员工7,sleep:7 等待了3000(ms),开始吃饭了!
  77. 员工6,sleep:6 等待了4000(ms),开始吃饭了!
  78. 员工5,sleep:5 等待了5000(ms),开始吃饭了!
  79. 员工4,sleep:4 等待了6000(ms),开始吃饭了!
  80. 员工9,sleep:9 等待了999(ms),开始吃饭了!
  81. 员工8,sleep:8 等待了1999(ms),开始吃饭了!

第一次规则被打乱了,过了一会导游重建了规则(cyclicBarrier.reset();),接着又重来来了一次模拟等待吃饭的操作,正常了。

CountDownLatch和CyclicBarrier的区别

还是举例子说明一下:

CountDownLatch示例

主管相当于 CountDownLatch,干活的小弟相当于做事情的线程。

老板交给主管了一个任务,让主管搞完之后立即上报给老板。主管下面有10个小弟,接到任务之后将任务划分为10个小任务分给每个小弟去干,主管一直处于等待状态(主管会调用await()方法,此方法会阻塞当前线程),让每个小弟干完之后通知一下主管(调用countDown()方法通知主管,此方法会立即返回),主管等到所有的小弟都做完了,会被唤醒,从await()方法上苏醒,然后将结果反馈给老板。期间主管会等待,会等待所有小弟将结果汇报给自己。

而CyclicBarrier是一批线程让自己等待,等待所有的线程都准备好了,自己才能继续。

java高并发系列

高并发系列连载中,感兴趣的加我微信itsoku,一起交流,关注公众号:路人甲Java,每天获取最新文章,送年薪50万java学习路线视频!

java高并发系列 - 第17天:JUC中的循环栅栏CyclicBarrier常见的6种使用场景及代码示例的更多相关文章

  1. java高并发系列 - 第13天:JUC中的Condition对象

    本文目标: synchronized中实现线程等待和唤醒 Condition简介及常用方法介绍及相关示例 使用Condition实现生产者消费者 使用Condition实现同步阻塞队列 Object对 ...

  2. 跟着阿里p7一起学java高并发 - 第19天:JUC中的Executor框架详解1,全面掌握java并发核心技术

    这是java高并发系列第19篇文章. 本文主要内容 介绍Executor框架相关内容 介绍Executor 介绍ExecutorService 介绍线程池ThreadPoolExecutor及案例 介 ...

  3. java高并发系列 - 第12天JUC:ReentrantLock重入锁

    java高并发系列 - 第12天JUC:ReentrantLock重入锁 本篇文章开始将juc中常用的一些类,估计会有十来篇. synchronized的局限性 synchronized是java内置 ...

  4. java高并发系列 - 第20天:JUC中的Executor框架详解2之ExecutorCompletionService

    这是java高并发系列第20篇文章. 本文内容 ExecutorCompletionService出现的背景 介绍CompletionService接口及常用的方法 介绍ExecutorComplet ...

  5. java高并发系列 - 第14天:JUC中的LockSupport工具类,必备技能

    这是java高并发系列第14篇文章. 本文主要内容: 讲解3种让线程等待和唤醒的方法,每种方法配合具体的示例 介绍LockSupport主要用法 对比3种方式,了解他们之间的区别 LockSuppor ...

  6. java高并发系列 - 第15天:JUC中的Semaphore,最简单的限流工具类,必备技能

    这是java高并发系列第15篇文章 Semaphore(信号量)为多线程协作提供了更为强大的控制方法,前面的文章中我们学了synchronized和重入锁ReentrantLock,这2种锁一次都只能 ...

  7. java高并发系列 - 第16天:JUC中等待多线程完成的工具类CountDownLatch,必备技能

    这是java高并发系列第16篇文章. 本篇内容 介绍CountDownLatch及使用场景 提供几个示例介绍CountDownLatch的使用 手写一个并行处理任务的工具类 假如有这样一个需求,当我们 ...

  8. java高并发系列 - 第21天:java中的CAS操作,java并发的基石

    这是java高并发系列第21篇文章. 本文主要内容 从网站计数器实现中一步步引出CAS操作 介绍java中的CAS及CAS可能存在的问题 悲观锁和乐观锁的一些介绍及数据库乐观锁的一个常见示例 使用ja ...

  9. java高并发系列 - 第22天:java中底层工具类Unsafe,高手必须要了解

    这是java高并发系列第22篇文章,文章基于jdk1.8环境. 本文主要内容 基本介绍. 通过反射获取Unsafe实例 Unsafe中的CAS操作 Unsafe中原子操作相关方法介绍 Unsafe中线 ...

随机推荐

  1. window8 飘带与页面切换效果

    演示效果如下 用鼠标点击滑动试试就能看到效果了 ^_^ iscroll 不仅可以做到自然滚动条的效果,看官方文档还可以用来做页面切换的效果,很好很强大. 所以我结合流行的飘带元素做了个简单的例子.. ...

  2. 记录一次PHP项目报502的问题

    问题描述 最近有台服务器偶尔会报502错误,虽然量不多,每天就几十个,但是也必须得找到原因,避免让小问题变成大问题. 排查过程 502错误的原因,一般是对用户访问请求的响应超时造成的,一开始以为是请求 ...

  3. java-mysql(1)

    用java写过不少单侧,用到的数据存储也是用xml或者直接文件,但是关于数据库这块很少用到,最近就学习了下java链接mysql数据库. 第一:创建一个测试用的数据库 Welcome to the M ...

  4. SpringBoot整合日志框架LogBack

    日志可以记录我们应用程序的运行情况,我们可以通过日志信息去获取应用程序更多的信息.常用处理java日志的组件有:slf4j.log4j.logback.common-logging等.其中log4j是 ...

  5. 10 jQuery的事件绑定和解绑

    1.绑定事件 语法: bind(type,data,fn) 描述:为每一个匹配元素的特定事件(像click)绑定一个事件处理器函数. 参数解释: type (String) : 事件类型 data ( ...

  6. 02-MySQL的安装和管理

    # mysql的安装和基本管理 # 01 数据库管理软件分类 ''' 分两大类: 关系型:如sqllite,db2,oracle,access,sql server,MySQL,注意:sql语句通用 ...

  7. java垃圾回收机制整理

    一.垃圾回收器和finalize() java垃圾回收器只负责回收无用对象占据的内存资源.但是如果你的对象不是通过 new 创建的(所有的new 对象都往堆中开辟资源,在一个地方,方便清理/管理资源) ...

  8. [UWP]从头开始创建并发布一个番茄钟

    1. 自己用的番茄钟自己做 在PC上我一直使用"小番茄"作为我的番茄钟软件,我把它打开后放在副显示器最大化,这样不仅可以让它尽到本分,而且还可以告诉我的同事"我正在专心工 ...

  9. java中更新文件时,指定原文件的编码格式,防止编码格式不对,造成乱码

    1.pom中添加引入cpdetector(一个可以自动检测文本编码格式的项目) //pom中添加引入cpdetector(一个可以自动检测文本编码格式的项目) <dependency> & ...

  10. springboot+redis+Interceptor+自定义annotation实现接口自动幂等

    前言: 在实际的开发项目中,一个对外暴露的接口往往会面临很多次请求,我们来解释一下幂等的概念:任意多次执行所产生的影响均与一次执行的影响相同.按照这个含义,最终的含义就是 对数据库的影响只能是一次性的 ...