在Java中如何利用CountDownLatch控制线程协同

来源:AI视频音频作者:盲改大师头衔:程序员
导读:本期聚焦于小伙伴创作的《在Java中如何利用CountDownLatch控制线程协同》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《在Java中如何利用CountDownLatch控制线程协同》有用,将其分享出去将是对创作者最好的鼓励。

在Java并发编程场景中,经常需要让一个或多个线程等待其他线程完成操作后再继续执行,CountDownLatch就是专门用来解决这类线程协同问题的工具类,它位于java.util.concurrent包下,基于AQS(抽象队列同步器)实现,通过计数器的方式控制线程的阻塞和唤醒。

在Java中如何利用CountDownLatch控制线程协同

CountDownLatch核心概念

CountDownLatch内部维护了一个计数器,初始化时指定计数器的初始值。当调用countDown()方法时,计数器的值会减1;当调用await()方法的线程会阻塞,直到计数器的值减到0时,所有阻塞在await()方法上的线程都会被唤醒继续执行。

它的核心构造方法和常用方法如下:

  • 构造方法CountDownLatch(int count),参数count是计数器的初始值,必须大于等于0
  • countDown():将计数器的值减1,如果减到0,会唤醒所有等待的线程
  • await():使当前线程阻塞,直到计数器的值变为0,或者线程被中断
  • await(long timeout, TimeUnit unit):使当前线程阻塞指定时间,如果时间内计数器变为0则唤醒,否则超时后继续执行
  • getCount():返回当前计数器的值

CountDownLatch使用步骤

使用CountDownLatch控制线程协同通常分为三步:

  1. 创建CountDownLatch实例,指定初始计数器值,这个值通常对应需要等待完成的线程数量或者需要完成的任务数量
  2. 在需要等待其他线程的线程中调用await()方法,让当前线程进入阻塞状态
  3. 在其他线程完成对应任务后调用countDown()方法,每次调用计数器减1,当计数器归零时,阻塞的线程会被唤醒

典型使用场景与代码示例

场景一:等待所有子线程完成任务后主线程再继续

比如我们需要启动3个子线程分别处理不同的任务,所有子线程都处理完成后,主线程再进行结果汇总,就可以用CountDownLatch实现。

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CountDownLatchDemo1 {
    public static void main(String[] args) throws InterruptedException {
        // 初始计数器为3,对应3个子线程
        CountDownLatch latch = new CountDownLatch(3);
        ExecutorService threadPool = Executors.newFixedThreadPool(3);
        
        // 提交3个任务到线程池
        for (int i = 1; i <= 3; i++) {
            int taskId = i;
            threadPool.submit(() -> {
                try {
                    System.out.println("子线程" + taskId + "开始处理任务");
                    // 模拟任务处理耗时
                    Thread.sleep(1000);
                    System.out.println("子线程" + taskId + "任务处理完成");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    // 任务完成,计数器减1
                    latch.countDown();
                }
            });
        }
        
        System.out.println("主线程等待所有子线程完成任务");
        // 主线程阻塞,等待计数器归零
        latch.await();
        System.out.println("所有子线程任务完成,主线程开始汇总结果");
        
        threadPool.shutdown();
    }
}

运行上述代码,输出顺序会是:主线程先打印等待信息,然后3个子线程依次开始处理任务,完成后主线程才会打印汇总信息,不会出现主线程先于子线程完成任务的情况。

场景二:控制多个线程同时开始执行

有时候我们需要让多个线程在准备完成后,同时开始执行某个操作,比如模拟并发请求的场景,可以先让所有线程准备好,然后通过CountDownLatch统一释放。

import java.util.concurrent.CountDownLatch;

public class CountDownLatchDemo2 {
    public static void main(String[] args) throws InterruptedException {
        // 计数器初始为1,用来控制所有线程同时开始
        CountDownLatch startLatch = new CountDownLatch(1);
        int threadNum = 5;
        
        for (int i = 1; i <= threadNum; i++) {
            int threadId = i;
            new Thread(() -> {
                try {
                    System.out.println("线程" + threadId + "准备完成,等待开始信号");
                    // 线程阻塞,等待开始信号
                    startLatch.await();
                    System.out.println("线程" + threadId + "开始执行任务,当前时间:" + System.currentTimeMillis());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
        
        // 主线程休眠1秒,确保所有子线程都进入等待状态
        Thread.sleep(1000);
        System.out.println("所有线程准备完毕,发送开始信号");
        // 计数器减1归零,所有等待的线程同时被唤醒
        startLatch.countDown();
    }
}

运行代码可以看到,所有线程的准备完成信息先打印,然后几乎在同一时间打印开始执行的信息,实现了线程同时启动的效果。

使用注意事项

  • CountDownLatch的计数器只能使用一次,当计数器归零后,再调用await()方法不会阻塞线程,也无法重置计数器的值。如果需要重复使用计数器,可以考虑使用CyclicBarrier。
  • 调用countDown()方法的次数不能超过初始计数器的值,否则计数器会变成负数,不过这种情况不会影响await()的唤醒逻辑,因为计数器到0时就已经唤醒所有等待线程了。
  • 如果等待的线程被中断,await()方法会抛出InterruptedException,需要根据业务场景处理中断逻辑,避免线程异常终止。
  • 在finally块中调用countDown()方法,确保即使任务执行出现异常,计数器也能正常减1,避免等待线程永久阻塞。

底层实现原理

CountDownLatch的底层是基于AQS实现的,AQS内部维护了一个state状态变量,CountDownLatch将state作为计数器的值。初始化时,state被设置为传入的count值;调用countDown()方法时,会通过CAS操作将state减1;调用await()方法时,会判断state是否为0,如果不为0,当前线程会被封装成节点加入AQS的等待队列中阻塞;当state减到0时,AQS会唤醒等待队列中的所有节点对应的线程。

这种实现方式保证了计数器的修改是线程安全的,同时等待线程的阻塞和唤醒逻辑也由AQS统一管理,保证了并发场景下的正确性。

CountDownLatchJava并发线程协同多线程协作修改时间:2026-07-08 04:54:24

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。