1 概述

1.1 定义

从字面意思理解,CyclicBarrier 是回环屏障的意思 ,它可以让一组线程全部达到一个状态后再全部同时执行,这里之所以叫作回环是因为当所 等待线程执行完毕,并重置 CyclicBarrier 的状态后它可以被重用。之所以 叫作屏障是因为线程调用 await 方法后就会被阻塞,这个阻塞点就称为屏障点,等所有线程都调用了 await 方法后,线程 就会冲破屏障,继续 下运行。

1.2 示例

1.2.1 简要示例

  1. public class CyclicBarrierTest {
  2. private static CyclicBarrier barrier = new CyclicBarrier(2, new Runnable() {
  3. @Override
  4. public void run() {
  5. //所有子线程执行完毕后再执行的任务
  6. System.out.println("主线程任务...");
  7. }
  8. });
  9. public static void main(String[] args) {
  10. ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
  11. .setNameFormat("demo-pool-%d").build();
  12. ExecutorService service = new ThreadPoolExecutor(4, 5, 1L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(10), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());
  13. service.submit(new Runnable() {
  14. @Override
  15. public void run() {
  16. try {
  17. System.out.println("task1阻塞前:" + barrier.getNumberWaiting());
  18. Thread.sleep(3000);
  19. barrier.await();
  20. System.out.println("task1阻塞后:" + barrier.getNumberWaiting());
  21. } catch (InterruptedException e) {
  22. e.printStackTrace();
  23. } catch (BrokenBarrierException e) {
  24. e.printStackTrace();
  25. }
  26. }
  27. });
  28. service.submit(new Runnable() {
  29. @Override
  30. public void run() {
  31. try {
  32. System.out.println("task2阻塞前:" + barrier.getNumberWaiting());
  33. barrier.await();
  34. System.out.println("task2阻塞后:" + barrier.getNumberWaiting());
  35. } catch (InterruptedException e) {
  36. e.printStackTrace();
  37. } catch (BrokenBarrierException e) {
  38. e.printStackTrace();
  39. }
  40. }
  41. });
  42. service.shutdown();
  43. }
  44. }

输出:
其第一 数为计数器初始值,第二个参数Runable是当计数器值为0时需要执行的任务。在main 函数里面首先创建了 1个大小 为2的线程池,然后添加两个子任 程池, 任务在执行 自己的逻辑后会调 用await 方法。一开始计数器值为 当第一个线程调用 awai 方法时,计数器值会递减 为1。由于此时计数器值不为0 ,所以 前线程就到了屏障点而被阻塞 然后第2个线程调用 await时,会进入 障,计数器值也会递减,现在计数器值为0这时就会去执行CyclicBanier 造函数中的任务,执行完毕后退出屏障点,并且唤醒被阻塞的第2个线程,这时候第1个线程也会退出屏障点继续向下运行。

1.2.2 复杂示例

假设一个任务由阶段一、阶段二和阶段三组成,每个线程要串行地执行阶段一、阶段二和阶段三,当多个线程执行该任务时,必须要保证所有线程的阶段一全部完成后才能进入阶段二执行, 当所有线程的阶段三全部完成后才能进入阶段3执行 下面使用
CyclicBanier 成这个需求

  1. public class CyclicBarrierTest2 {
  2. private static CyclicBarrier barrier = new CyclicBarrier(2);
  3. public static void main(String[] args) {
  4. ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
  5. .setNameFormat("demo-pool-%d").build();
  6. ExecutorService service =
  7. new ThreadPoolExecutor(5, 8, 1,
  8. TimeUnit.SECONDS, new ArrayBlockingQueue<>(10), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());
  9. for (int i = 0; i < 3; i++) {
  10. int finalI = i;
  11. service.submit(new Runnable() {
  12. @SneakyThrows
  13. @Override
  14. public void run() {
  15. System.out.println("线程池" + finalI + " begin:>>>" + Thread.currentThread().getName() + " number:" + barrier.getNumberWaiting() + " ;Parties:" + barrier.getParties());
  16. barrier.await();
  17. System.out.println("线程池" + finalI + " wait:>>>" + Thread.currentThread().getName() + " number:" + barrier.getNumberWaiting() + " ;Parties:" + barrier.getParties());
  18. barrier.await();
  19. System.out.println("线程池" + finalI + " after:>>>" + Thread.currentThread().getName() + " number:" + barrier.getNumberWaiting() + " ;Parties:" + barrier.getParties());
  20. }
  21. });
  22. }
  23. service.shutdown();
  24. }
  25. }

输出:

  1. 线程池0 begin:>>>demo-pool-0 number:0 ;Parties:2
  2. 线程池1 begin:>>>demo-pool-1 number:1 ;Parties:2
  3. 线程池1 wait:>>>demo-pool-1 number:0 ;Parties:2
  4. 线程池2 begin:>>>demo-pool-2 number:1 ;Parties:2
  5. 线程池0 wait:>>>demo-pool-0 number:1 ;Parties:2
  6. 线程池1 after:>>>demo-pool-1 number:1 ;Parties:2
  7. 线程池2 wait:>>>demo-pool-2 number:1 ;Parties:2
  8. 线程池2 after:>>>demo-pool-2 number:0 ;Parties:2
  9. 线程池0 after:>>>demo-pool-0 number:0 ;Parties:2