当前位置: 首页 > news >正文

CountDownLatch使用

基本概念

CountDownLatch 基于一个计数器实现,在创建 CountDownLatch 对象时需要指定一个初始计数,这个计数代表需要等待完成的操作数量。线程可以调用 CountDownLatchcountDown() 方法将计数器减 1,而其他线程可以调用 await() 方法进入等待状态,直到计数器的值变为 0 才会继续执行。

使用场景

  • 并行任务同步:当有多个并行任务需要完成,并且主线程需要等待所有并行任务都完成后才能继续执行后续操作时,可以使用 CountDownLatch。例如,在多线程下载文件的场景中,主线程需要等待所有子线程都下载完成后再进行文件的合并操作。
  • 资源初始化:在某些情况下,需要确保所有必要的资源都初始化完成后,主线程才能继续执行后续业务逻辑。可以使用 CountDownLatch 来协调资源初始化线程和主线程的执行顺序。

工作原理

  • 初始化:创建 CountDownLatch 对象时,需要传入一个整数作为计数器的初始值。
  • 计数递减:每个需要完成的任务线程在完成任务后调用 countDown() 方法,将计数器的值减 1。
  • 等待操作:调用 await() 方法的线程会被阻塞,直到计数器的值变为 0。计数器变为 0 后,所有等待的线程会被释放,继续执行后续代码。

代码示例

import java.util.concurrent.CountDownLatch;

public class CountDownLatchExample {
    public static void main(String[] args) throws InterruptedException {
        // 创建一个 CountDownLatch 对象,初始计数为 3
        CountDownLatch latch = new CountDownLatch(3);

        // 创建并启动 3 个工作线程
        for (int i = 0; i < 3; i++) {
            final int taskId = i;
            Thread worker = new Thread(() -> {
                try {
                    System.out.println("线程 " + taskId + " 开始执行任务");
                    // 模拟任务执行
                    Thread.sleep((long) (Math.random() * 1000));
                    System.out.println("线程 " + taskId + " 完成任务");
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                } finally {
                    // 任务完成,计数器减 1
                    latch.countDown();
                }
            });
            worker.start();
        }

        // 主线程等待所有工作线程完成任务
        System.out.println("主线程等待所有任务完成...");
        latch.await();
        System.out.println("所有任务已完成,主线程继续执行");
    }
}

代码解释

  1. 创建 CountDownLatch 对象CountDownLatch latch = new CountDownLatch(3); 表示需要等待 3 个任务完成。
  2. 启动工作线程:通过 for 循环创建并启动 3 个工作线程,每个线程模拟执行一个任务,任务完成后调用 latch.countDown() 方法将计数器减 1。
  3. 主线程等待:主线程调用 latch.await() 方法进入等待状态,直到计数器的值变为 0。
  4. 继续执行:当计数器的值变为 0 时,主线程被唤醒,继续执行后续代码。

注意事项

  • CountDownLatch 的计数器一旦初始化为某个值,就不能再重置。如果需要重置计数器,可以考虑使用 CyclicBarrier
  • await() 方法有两种形式:无参的 await() 会一直等待,直到计数器变为 0;带参数的 await(long timeout, TimeUnit unit) 会在指定的时间内等待,如果超过指定时间计数器仍未变为 0,线程会继续执行。

这种表述大体上抓住了 CountDownLatch 的核心功能,但存在一些细节需要更精准地说明,下面详细解释。

准确理解关键方法的作用

await() 方法

await() 方法会让调用它的线程(不一定是主线程,任何线程都可以调用)进入等待状态。当线程调用 await() 时,它会检查 CountDownLatch 内部计数器的值。如果计数器不为 0,线程就会被阻塞,进入等待状态;只有当计数器的值变为 0 时,线程才会从 await() 方法返回,继续执行后续代码。

countDown() 方法

每调用一次 countDown() 方法,CountDownLatch 的计数器就会减 1。不过,并不是每次调用 countDown() 都会唤醒其他线程。只有当计数器的值从大于 0 减为 0 时,才会唤醒所有在 await() 方法中等待的线程。

结合示例代码说明

import java.util.concurrent.CountDownLatch;

public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        // 创建一个 CountDownLatch 实例,初始计数器值为 3
        CountDownLatch latch = new CountDownLatch(3);

        // 启动 3 个工作线程
        for (int i = 0; i < 3; i++) {
            final int workerId = i;
            new Thread(() -> {
                try {
                    System.out.println("工作线程 " + workerId + " 开始工作");
                    // 模拟工作耗时
                    Thread.sleep(1000); 
                    System.out.println("工作线程 " + workerId + " 完成工作");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    // 工作完成,计数器减 1
                    latch.countDown(); 
                }
            }).start();
        }

        System.out.println("主线程等待所有工作线程完成工作...");
        // 主线程调用 await() 进入等待状态
        latch.await(); 
        System.out.println("所有工作线程已完成工作,主线程继续执行");
    }
}

代码执行流程分析

  1. 初始化:创建 CountDownLatch 对象,将计数器初始值设为 3。
  2. 启动工作线程:启动 3 个工作线程,每个线程模拟执行一些工作,完成后调用 countDown() 方法。
  3. 主线程等待:主线程调用 await() 方法,此时计数器值为 3,主线程进入等待状态。
  4. 工作线程完成任务:每个工作线程完成任务后调用 countDown(),计数器依次减 1。前两次调用 countDown() 时,计数器不为 0,不会唤醒等待线程;当第三次调用 countDown() 使计数器变为 0 时,会唤醒在 await() 方法中等待的主线程。
  5. 主线程继续执行:主线程从 await() 方法返回,继续执行后续代码。

综上所述,await() 方法让调用线程等待计数器变为 0,countDown() 方法用于减少计数器的值,仅当计数器变为 0 时才会唤醒等待线程。

简易版实现
public class MyCountDownLatch {

    private Integer count;

    public MyCountDownLatch(int count){
        this.count = count;
    }


    public synchronized void countDown(){
        count--;
        if (count == 0){
            notify();//唤醒等待队列中的一个线程
            //notifyAll();//唤醒等待队列中的所有线程
        }
    }

    public synchronized void await() throws InterruptedException{
        System.out.println(count);
        if (count != 0){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}

相关文章:

  • DeepSeek 开源周五个开源项目,引领 AI 创新?
  • 构建神经网络之Matplotlib(持续完善)
  • 生态安全相关
  • 【 开发知识点 一 】 随机数生成器 /dev/urandom 和 /dev/random
  • 《几何原本》命题I.1
  • aws(学习笔记第三十课) 练习使用transit gateway
  • yolov12 部署瑞芯微 rk3588、RKNN 部署工程难度小、模型推理速度快
  • 计算机毕业设计Python+DeepSeek-R1大模型游戏推荐系统 Steam游戏推荐系统 游戏可视化 游戏数据分析(源码+文档+PPT+讲解)
  • 计算机网络之传输层(tcp协议)
  • kubernetes 高可用集群搭建
  • Dify v1.0.0 里程碑版本正式亮相
  • Metal学习笔记九:光照基础
  • WPF高级 | WPF 与数据库交互:连接、查询与数据更新
  • 《操作系统 - 清华大学》 8 -11:进程管理:上下文切换
  • 从零开始用react + tailwindcss + express + mongodb实现一个聊天程序(六) 导航栏 和 个人信息设置
  • C语言基础系列【17】位域
  • 【NLP 30、大模型中的 ”Token“】
  • Tagr 5 for Mac v5.8.0 [MAS] 音频标签编辑工具 支持M、Intel芯片
  • 4.万事开头难—第一个Java程序
  • 使用Maven搭建Spring Boot框架
  • 做名片去哪个网站/深圳seo排名优化
  • 武汉网站整合营销什么价格/制作公司网站大概多少钱
  • 做编程网站有哪些/2345网址导航用户中心
  • 东莞数据线厂家东莞网站建设/百度收录教程
  • 公司邮箱如何申请/淘宝seo是什么意思
  • 仙桃网站制作/职业技能培训网上平台