面试官:讲一讲你理解的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都是用于控制并发的工具类,都可以理解成维护的就是一个计数器,可以用来控制线程的执行。

相关推荐
卷毛的技术笔记10 小时前
告别硬编码!Spring AI Alibaba 实现 AI Agent 智能工具调用(Tool Calling)
java·人工智能·后端·python·spring·ai编程
会编程的土豆10 小时前
Go 语言反射(Reflection)详解
开发语言·后端·golang
Cosolar10 小时前
从零写一个 Attention Is All You Need
人工智能·面试·架构
喵个咪10 小时前
GoWind Toolkit Go后端代码生成 完整全流程实战
后端·go·orm
basketball61611 小时前
Go 语言从入门到进阶:4. 数组和MAP使用方法总结
开发语言·后端·golang
qq_25183645711 小时前
SpringBoot+Vue 共享电池柜管理系统 完整实现 前后端分离项目实战 完整代码
vue.js·spring boot·后端
zhangxingchao11 小时前
AI 大模型核心六:量化、Workflow 与 Agent、多轮 RAG
前端·人工智能·后端
IT_陈寒12 小时前
Vite打包时遇到的坑,原来问题出在这里
前端·人工智能·后端
jiayong2313 小时前
AI架构师面试题库 - 完整汇总文档
人工智能·面试·职场和发展
ayqy贾杰14 小时前
基层管理的三板斧,在AI时代行不通了
前端·后端·团队管理