还在傻傻分不清CountDownLatch和CyclicBarrier吗,看过来

27 阅读3分钟

一、CountDownLatch

1、CountDownLatch一般用于在做某件事A之前,必须要完成另外一批事情,所以事件A必须阻塞在那

2、例如:

(1)、excel导出

多个sheet页数据填充,每个sheet页可以看做一个线程

主线程就是excel文件流输出

(2)、需要重多个三方接口请求数据,组装输出

请求每个三方接口就时一个线程

主线程就是从结果中取数据组装

3、代码实现6个小孩出门之后,老师(主线程)锁门

public static void main(String[] args) throws InterruptedException {
    CountDownLatch countDownLatch = new CountDownLatch(6);
    for (int i = 1; i <=6 ; i++) {
        new Thread(()->{
            System.out.println("child:" + Thread.currentThread().getName()+" Go out");
            countDownLatch.countDown(); // 数量-1
        },String.valueOf(i)).start();
    }
    countDownLatch.await(); // 等待计数器归零,然后再向下执行
    System.out.println("teacher close door");
}

image.png

当然,CountDownLatch也可以用来阻塞一批线程

    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(6);
        for (int i = 1; i <=6 ; i++) {
            new Thread(()->{
                try {
                    TimeUnit.SECONDS.sleep(3);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("child:" + Thread.currentThread().getName()+" Go out");
                countDownLatch.countDown(); // 数量-1
            },String.valueOf(i)).start();
        }
​
​
        for (int i = 1; i <=6 ; i++) {
            new Thread(()->{
                try {
                    countDownLatch.await(); // 等待计数器归零,然后再向下执行
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("teacher close door");
            },String.valueOf(i)).start();
        }
​
//        countDownLatch.await(); // 等待计数器归零,然后再向下执行
//        System.out.println("teacher close door");
    }
​

image.png

二、CyclicBarrier

CyclicBarrier与CountDownLatch类似,也可以实现做某件事A之前,必须要完成另外一批事情

但是CyclicBarrier强调的是这一批线程达到某一个屏障,也就是阻塞在那里

(Barrier:障碍;屏障;隔阂;关卡;阻力;分界线;难以逾越的数量(或水平、数目))

代码实现王者荣耀十人加载之后才能开启游戏

public static void main(String[] args) {
    CyclicBarrier cyclicBarrier = new CyclicBarrier(10,()->{
        System.out.println("全军出击");
    });
    for (int i = 1; i <=10 ; i++) {
        final int temp = i;
        new Thread(()->{
            System.out.println(Thread.currentThread().getName() + "玩家"+ temp+"号已经加载100%");  // 局部内部类只能访问常量,所以转成temp
            try {
                cyclicBarrier.await(); // 等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }).start();
​
    }
}

image.png

看到这里,大家可能觉得跟CountDownLatch没啥区别,我们继续看

王者荣耀开局十个人加载,每个人的加载过程就是一个线程,其中某一个人加载好了,并不能直接开打,得等到十个人都加载好了,才能开打

public static void main(String[] args) {
    /**
         * 王者荣耀加载
         */
    CyclicBarrier cyclicBarrier = new CyclicBarrier(10,()->{
        System.out.println("全军出击");
    });
    for (int i = 1; i <=10 ; i++) {
        final int temp = i;
        new Thread(()->{
            System.out.println(Thread.currentThread().getName() + "玩家"+ temp+"号已经加载100%");  // 局部内部类只能访问常量,所以转成temp
            try {
                cyclicBarrier.await(); // 等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"玩家"+ temp+"号已经不可阻挡!!");
        }).start();
​
    }
}

image.png

可以看到每个线程达到屏障那里就直接等待在那了,等所有人加载好了,才能开打,当然CyclicBarrier的构造器也可以只传一个数字,不需要再多一个Runnable的实现类,可以理解为十名玩家都加载到100%之后,就直接开打了,不需要系统来喊全军出击了。

个人理解CountDownLatch主要是用于阻塞另外一个或者另外一批线程,而CyclicBarrier主要是用于阻塞自己这一批线程。