面试官:讲一讲你理解的CountDownLatch 和 CyclicBarrier

前言

CountDownLatch 和 CyclicBarrier 这两个东西,面试必问,倒也不难,反正大家都是背的,面试官也知道你是背的,你也知道面试官知道你是背的,面试官也知道你知道面试官知道你是背的.......

无限套娃

CountDownLatch

CountDownLatch,闭锁,是一个同步工具类,用来协调多个线程之间的同步。

主要有两个方法,当一个或者多个线程调用 await 方法时,调用线程会被阻塞,其他线程调用 countDown 方法计数器 - 1,当计数器的值变为0时,调用 await 方法被阻塞的线程会被唤醒,继续执行

在完成一组计算时,允许一个线程或者多个一直等待,直到其他线程全部完成。

例子:将一个六人的队伍集合带回,必须人齐了才能带回

如果不使用闭锁的话:

csharp 复制代码
public class CountDownLatchTest {
​
    public static void main(String[] args) {
        for (int i = 0; i < 6; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "到了");
            }).start();
        }
​
        System.out.println(Thread.currentThread().getName() + "六人队伍齐了");
    }
​
}

输出结果:

css 复制代码
Thread-0到了
Thread-4到了
Thread-3到了
Thread-2到了
Thread-1到了
main六人队伍齐了
Thread-5到了

很显然,线程是竞争执行的,不一定哪个块,无法保证主线程最后执行。

我们加上 CountDownLatch

scss 复制代码
    public static void main(String[] args) {
        CountDownLatch countDownLatch = new CountDownLatch(6);
        for (int i = 0; i < 6; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "到了");
                countDownLatch.countDown();
            }).start();
        }
​
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(Thread.currentThread().getName() + "六人队伍齐了");
    }

输出结果:

css 复制代码
Thread-0到了
Thread-2到了
Thread-3到了
Thread-5到了
Thread-1到了
Thread-4到了
main六人队伍齐了

CyclicBarrier

CyclicBarrier,循环屏障,它允许一组线程互相等待,直到到达某个公共屏障点。好比到了某个条件之后,所有线程一起执行

例子:还是6个人去干饭,人齐了去干饭,人齐之后下达命令,然后大家干饭

csharp 复制代码
public class CyclicBarrierTest {
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(6, () -> {
            System.out.println("人齐了,下达干饭命令,出发干饭!");
        });
        for (int i = 0; i < 6; i++) {
            int finalI = i;
            new Thread(() -> {
                System.out.println("老"+ finalI +"来到");
                try {
                    cyclicBarrier.await();
                    System.out.println("老"+ finalI +"干饭");
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                } catch (BrokenBarrierException e) {
                    throw new RuntimeException(e);
                }
            }).start();
        }
​
    }
}

输出结果:

老0来到
老3来到
老2来到
老1来到
老4来到
老5来到
人齐了,下达干饭命令,出发干饭!
老5干饭
老0干饭
老3干饭
老2干饭
老4干饭
老1干饭

可以看到等人齐了之后,输出干饭命令,然后各个线程再去执行剩下的代码

区别和联系

可以看一下 CountDownLatch 的代码

csharp 复制代码
public class CountDownLatchTest {
​
    public static void main(String[] args) {
        CountDownLatch countDownLatch = new CountDownLatch(6);
        for (int i = 0; i < 6; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "到了");
                countDownLatch.countDown();
                System.out.println(Thread.currentThread().getName() + "到了之后出发");
            }).start();
        }
​
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(Thread.currentThread().getName() + "六人队伍齐了");
    }
​
}

输出结果:

css 复制代码
Thread-3到了
Thread-0到了
Thread-0到了之后出发
Thread-5到了
Thread-5到了之后出发
Thread-2到了
Thread-2到了之后出发
Thread-4到了
Thread-4到了之后出发
Thread-3到了之后出发
Thread-1到了
Thread-1到了之后出发
main六人队伍齐了

可以看到使用 CountDownLatch ,调用 countDown()方法之后,之后的代码仍会继续执行,CountDownLatch 只能保证一个线程或几个线程最后执行,其他调用countDown()方法的线程早就执行结束了。

而 CyclicBarrier 的 await() 方法会阻塞,等所有线程执行结束后,所有调用 await() 线程会一起执行。

相同点:

CountDownLatch 和 CyclicBarrier 都具有await()方法,执行此方法都会阻塞当前线程,当达到某种条件才能继续执行

不同点:

  • CountDownLatch 是一次性的,CyclicBarrier 是可循环利用的
  • CountDownLatch 的计数器只能使用一次,而CyclicBarrier 的计数器可以使用 reset方法重置,能够处理更为复杂的场景
  • CountDownLatch一般用于某个线程A等待若干个其他线程执行完任务之后,它才执行;而CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行;
  • CountDownLatch强调一个线程等多个线程完成某件事情。CyclicBarrier是多个线程互等,等大家都完成,再携手共进

总结和提高

CountDownLatch与CyclicBarrier都是用于控制并发的工具类,都可以理解成维护的就是一个计数器,可以用来控制线程的执行。

相关推荐
camellias_2 小时前
SpringBoot(二十三)SpringBoot集成JWT
java·spring boot·后端
tebukaopu1482 小时前
springboot如何获取控制层get和Post入参
java·spring boot·后端
昔我往昔2 小时前
SpringBoot 创建对象常见的几种方式
java·spring boot·后端
灭掉c与java2 小时前
第三章springboot数据访问
java·spring boot·后端
啊松同学2 小时前
【Java】设计模式——工厂模式
java·后端·设计模式
枫叶_v3 小时前
【SpringBoot】20 同步调用、异步调用、异步回调
java·spring boot·后端
源码12154 小时前
ASP.NET MVC宠物商城系统
后端·asp.net·宠物
nameofworld5 小时前
前端面试笔试(二)
前端·javascript·面试·学习方法·数组去重
摇光935 小时前
promise
前端·面试·promise
Ai 编码助手5 小时前
Go语言 实现将中文转化为拼音
开发语言·后端·golang