1 引言

少侠在之前的一篇博客介绍闭锁CountDownLatch的时候有提到CyclicBarrier,那么它究竟是什么呢?本篇文章和你们一起揭开它神秘的面纱。
关于CountDownLatch的原理和用法可以阅读下篇,附传送门↓:
Java并发编程(三):CountDownLatch(闭锁)原理及最佳实践 从字面上的意思可以知道,这个类的中文意思是“循环栅栏”。大概的意思就是一个可循环利用的屏障。
它的作用就是会让所有线程都等待完成后才会继续下一步行动

举个例子,就像我们打王者荣耀,每个人的宽带网速不同,游戏开始受限于每个人进入游戏的时机,只有当所有人都进入游戏才会开局,这里的开局就是CyclicBarrier(这里仅仅是举例,并不代表王者荣耀游戏开局就是通过CyclicBarrier实现的)。

2 基本用法

2.1 构造方法:
public CyclicBarrier(int parties) 
 public CyclicBarrier(int parties, Runnable barrierAction)

说明:

  • parties表示参与的线程个数
  • 第二个构造方法有个Runnable类型的参数,表示当所有线程都完成后再继续进行的任务。
2.2 常用方法
public int await() throws InterruptedException, BrokenBarrierException
public int await(long timeout, TimeUnit unit) throws InterruptedException, BrokenBarrierException, TimeoutException

说明:

  • 线程调用await()表示自己已经到达栅栏
  • BrokenBarrierException表示栅栏已经被破坏,破坏的原因可能是其中一个线程await()时被中断或者超时
2.3 一个小栗子

假设A、B、C、D、E、F六个人约好了今天去春游,把预约的车子比喻成CyclicBarrier,必须六个人都到了才能开始发车,只要缺一个人,其他先到达的人必须等待。如何实现这个场景呢?
来看代码:

/**
 * @author Carson
 * @date 2020/4/14 19:13
 */
public class Main {
    public static void main(String[] args) {
        int threadNum = 6;
        //初始化循环栅栏,含6个线程,当6个线程都到栅栏位置则执行这里的“集合完毕...”
        final CyclicBarrier cyclicBarrier = new CyclicBarrier(threadNum, new Runnable() {
            @Override
            public void run() {
                System.out.println("\r\n" + Thread.currentThread().getName() + " 集合完毕,准备发车..."+"\r\n");
            }
        });
        //初始化线程池
        final ScheduledThreadPoolExecutor threadPoolExecutor = new ScheduledThreadPoolExecutor(threadNum,
                new BasicThreadFactory.Builder().daemon(true).build());
        for (int i = 0; i < threadNum; i++) {
            //提交线程
            threadPoolExecutor.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + " 前来集合...");
                    try {
                        //每次提交的时候调用await()操作,通知系统自己已经到达栅栏处
                        cyclicBarrier.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                    //当所有线程都冲破栅栏之后继续执行该操作
                    System.out.println(Thread.currentThread().getName() + " 开始春游...");
                }
            });
        }
    }
}

输出结果也好理解:

pool-1-thread-3 前来集合...
pool-1-thread-5 前来集合...
pool-1-thread-1 前来集合...
pool-1-thread-4 前来集合...
pool-1-thread-2 前来集合...
pool-1-thread-6 前来集合...

pool-1-thread-6 集合完毕,准备发车...

pool-1-thread-6 开始春游...
pool-1-thread-3 开始春游...
pool-1-thread-2 开始春游...
pool-1-thread-4 开始春游...
pool-1-thread-1 开始春游...
pool-1-thread-5 开始春游...

由结果可以看出,6个人全部到齐的时候才执行集合完毕的操作,并且该操作是由最后一个冲破栅栏的线程执行的(如本例是pool-1-thread-6完成执行的)。当所有人都冲破栅栏之后,于是各个线程都依次执行它们后续的操作。

3 CyclicBarrierCountDownLatch的区别

  • CyclicBarrier底层的计数器是可以通过reset()方法重置以达到循环使用的目的,而CountDownLatch计数器不可被重置。
  • CyclicBarrier用于等待其他线程运行到栅栏位置(当所有线程都运行到栅栏位置则冲破栅栏),CountDownLatch允许一个或多个线程等待一组事件的发生
  • CyclicBarrier中,所有参与的线程职责是一样的;而CountDownLatch中参与的线程的职责是不一样,有的在倒计时,有的在等待倒计时结束。