惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

L
LangChain Blog
V
V2EX
爱范儿
爱范儿
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Martin Fowler
Martin Fowler
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Apple Machine Learning Research
Apple Machine Learning Research
WordPress大学
WordPress大学
有赞技术团队
有赞技术团队
宝玉的分享
宝玉的分享
Last Week in AI
Last Week in AI
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
罗磊的独立博客
小众软件
小众软件
Vercel News
Vercel News
博客园 - 司徒正美
阮一峰的网络日志
阮一峰的网络日志
V
Visual Studio Blog
J
Java Code Geeks
P
Proofpoint News Feed
MongoDB | Blog
MongoDB | Blog
B
Blog
美团技术团队
量子位

博客园 - 野鹤闲人

ubantu 耳机调试 keepalived+nginx msg广播到同一个group中,怎么保证所有consumer都消费到,Rocketmq和rabbitmq 分别是怎么做的 mysql优化语句时,关注哪几列 extra要关注吗 机器缩容要注意哪些问题 IO模型有哪几种 ConcurrentSkipListMap 并发安全实现 TreeMap hive 小文件优化 redis缓存雪崩问题解决 CAS原理 docker的常用命令 k8s的常用组件,和命令 mysql B+树 如果有3层,能保存多少数据 如何保证rabbitmq或kafka 消息不丢失,从生产到消费端全过程 java排查工具 JAVA 强软弱虚引用 git Spring 事务失效
CountDownLatch/CyclicBarrier 区别
野鹤闲人 · 2026-01-26 · via 博客园 - 野鹤闲人

 CountDownLatch 和 CyclicBarrier 这两个 Java 并发工具类的核心区别,我会从定义、使用场景、核心特性等方面帮你清晰梳理。

一、核心概念与本质区别

先通过简单的比喻理解两者:

  • CountDownLatch:像一场考试的 “交卷倒计时”,监考老师(主线程)要等所有学生(子线程)都交卷(执行完任务)才能收卷,倒计时归 0 后就结束,只能使用一次。
  • CyclicBarrier:像一群人约好 “集齐再出发” 的旅游团,所有人到齐(线程都到达屏障点)后才能一起出发(执行后续任务),出发后屏障可以重置,能重复使用。

二、详细区别对比

特性CountDownLatchCyclicBarrier
核心作用 一个 / 多个线程等待其他线程完成操作 多个线程互相等待,直到所有线程都到达屏障点
可复用性 计数器归 0 后无法重置,只能用一次 屏障触发后可自动重置,能重复使用
触发条件 计数器 countDown() 到 0 触发 指定数量的线程调用 await() 到达屏障触发
等待方 可以是一个或多个线程等待 参与的线程互相等待(都是等待方)
额外功能 无回调函数 支持屏障触发时执行指定的回调任务
底层实现 基于 AQS 的共享模式 基于 ReentrantLock + Condition

三、代码示例(直观理解)

1. CountDownLatch 示例(一次性等待)

场景:主线程等待 3 个工作线程全部完成任务后,再执行汇总操作。

import java.util.concurrent.CountDownLatch;

public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        // 初始化计数器为 3(对应 3 个工作线程)
        CountDownLatch latch = new CountDownLatch(3);

        // 启动 3 个工作线程
        for (int i = 1; i <= 3; i++) {
            new Thread(() -> {
                try {
                    System.out.println(Thread.currentThread().getName() + " 执行任务中...");
                    Thread.sleep(1000); // 模拟任务耗时
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    latch.countDown(); // 任务完成,计数器减 1
                    System.out.println(Thread.currentThread().getName() + " 任务完成,计数器剩余: " + latch.getCount());
                }
            }, "线程" + i).start();
        }

        // 主线程等待计数器归 0
        latch.await();
        System.out.println("所有工作线程任务完成,主线程执行汇总操作");
    }
}

输出(顺序可能略有不同):

线程1 执行任务中...
线程2 执行任务中...
线程3 执行任务中...
线程1 任务完成,计数器剩余: 2
线程2 任务完成,计数器剩余: 1
线程3 任务完成,计数器剩余: 0
所有工作线程任务完成,主线程执行汇总操作

2. CyclicBarrier 示例(可重复使用)

场景:3 个运动员线程,每次都要等所有人到起跑线(屏障点)才能一起起跑,且支持重复比赛(屏障重置)。

import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierDemo {
    public static void main(String[] args) {
        // 初始化屏障:3 个线程到达后触发,且触发时执行回调任务(打印起跑提示)
        CyclicBarrier barrier = new CyclicBarrier(3, () -> {
            System.out.println("===== 所有运动员已就位,开始起跑!=====\n");
        });

        // 定义运动员任务
        Runnable runner = () -> {
            try {
                for (int i = 1; i <= 2; i++) { // 模拟 2 轮比赛
                    System.out.println(Thread.currentThread().getName() + " 到达起跑线,等待其他运动员(第" + i + "轮)");
                    barrier.await(); // 到达屏障,等待其他线程
                    System.out.println(Thread.currentThread().getName() + " 开始跑步(第" + i + "轮)");
                    Thread.sleep(500); // 模拟跑步耗时
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        };

        // 启动 3 个运动员线程
        new Thread(runner, "运动员A").start();
        new Thread(runner, "运动员B").start();
        new Thread(runner, "运动员C").start();
    }
}

输出:

运动员A 到达起跑线,等待其他运动员(第1轮)
运动员B 到达起跑线,等待其他运动员(第1轮)
运动员C 到达起跑线,等待其他运动员(第1轮)
===== 所有运动员已就位,开始起跑!=====

运动员C 开始跑步(第1轮)
运动员A 开始跑步(第1轮)
运动员B 开始跑步(第1轮)
运动员A 到达起跑线,等待其他运动员(第2轮)
运动员B 到达起跑线,等待其他运动员(第2轮)
运动员C 到达起跑线,等待其他运动员(第2轮)
===== 所有运动员已就位,开始起跑!=====

运动员C 开始跑步(第2轮)
运动员B 开始跑步(第2轮)
运动员A 开始跑步(第2轮)

四、使用场景总结

  • CountDownLatch 适合:
    1. 主线程等待多个子线程完成初始化 / 任务后,再执行后续逻辑(如程序启动时加载多个配置文件);
    2. 多个子线程等待主线程发出 “开始” 信号后同时执行(可初始化 latch 为 1,主线程调用 countDown () 释放所有等待的子线程)。
  • CyclicBarrier 适合:
    1. 多个线程需要同步步调、分批执行任务的场景(如多线程分批处理数据,每批都要等所有线程准备好);
    2. 重复的同步场景(如定时任务中,每次都要等所有线程就绪后再执行)。

总结

  1. 核心差异:CountDownLatch 是 “单向倒计时,一次性使用”,侧重 “等待其他线程完成”;CyclicBarrier 是 “互相等待,可重复使用”,侧重 “所有线程同步出发”。
  2. 复用性:CountDownLatch 计数器归 0 后失效,CyclicBarrier 触发后自动重置,可循环使用。
  3. 回调能力:CyclicBarrier 支持屏障触发时执行回调任务,CountDownLatch 无此功能。