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

JUC并发包CountDownLatch减法计数器的使用实例(多线程)

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;// A类:定义CountDownLatch
class A {private final CountDownLatch latch;public A(int count) {this.latch = new CountDownLatch(count);}public CountDownLatch getLatch() {return latch;}
}// B类:调用countDown()方法
class B {private final A a;public B(A a) {this.a = a;}public void countB() {System.out.println(Thread.currentThread().getName() + " 执行countB()方法,准备调用countDown()");a.getLatch().countDown();System.out.println(Thread.currentThread().getName() + " 已调用countDown(),剩余计数: " + a.getLatch().getCount());}
}// C类:调用await()方法阻塞当前线程
class C {private final A a;public C(A a) {this.a = a;}public void awaitC() throws InterruptedException {System.out.println(Thread.currentThread().getName() + " 执行awaitC()方法,开始等待...");// 等待,最多等待5秒boolean result = a.getLatch().await(5, TimeUnit.SECONDS);if (result) {System.out.println(Thread.currentThread().getName() + " 等待完成,所有countDown()已调用");} else {System.out.println(Thread.currentThread().getName() + " 等待超时,仍有countDown()未调用");}}
}// 主类:演示CountDownLatch的使用
public class Main {public static void main(String[] args) {// 创建A类实例,初始计数为3A a = new A(3);// 创建B类和C类实例B b = new B(a);C c = new C(a);// 创建并启动3个线程来调用B类的countB()方法for (int i = 0; i < 3; i++) {new Thread(() -> {try {// 模拟一些工作Thread.sleep((long) (Math.random() * 3000));b.countB();} catch (InterruptedException e) {e.printStackTrace();}}, "Thread-B-" + i).start();}// 主线程调用C类的awaitC()方法等待try {c.awaitC();} catch (InterruptedException e) {e.printStackTrace();}finally{a.getLatch().countDown();}System.out.println("主线程继续执行...");}
}

代码说明:

  1. A 类

    • 包含一个 CountDownLatch 实例,通过构造函数初始化计数
    • 提供 getLatch () 方法供其他类访问 CountDownLatch
  2. B 类

    • 持有 A 类的引用
    • countB () 方法调用 CountDownLatch 的 countDown () 方法
  3. C 类

    • 持有 A 类的引用
    • awaitC () 方法调用 CountDownLatch 的 await (long timeout, TimeUnit unit) 方法,最多等待 5 秒
  4. Main 类

    • 创建 A 类实例,初始计数为 3
    • 创建 B 类和 C 类实例
    • 启动 3 个线程分别调用 B 类的 countB () 方法
    • 主线程调用 C 类的 awaitC () 方法等待所有 countDown () 调用完成
    • 输出结果显示等待是否成功

运行结果示例:

Thread-B-0 执行countB()方法,准备调用countDown()
Thread-B-0 已调用countDown(),剩余计数: 2
Thread-B-1 执行countB()方法,准备调用countDown()
Thread-B-1 已调用countDown(),剩余计数: 1
Thread-B-2 执行countB()方法,准备调用countDown()
Thread-B-2 已调用countDown(),剩余计数: 0
main 执行awaitC()方法,开始等待...
main 等待完成,所有countDown()已调用
主线程继续执行...

或者(如果超时):

main 执行awaitC()方法,开始等待...
Thread-B-0 执行countB()方法,准备调用countDown()
Thread-B-0 已调用countDown(),剩余计数: 2
main 等待超时,仍有countDown()未调用
主线程继续执行...
Thread-B-1 执行countB()方法,准备调用countDown()
Thread-B-1 已调用countDown(),剩余计数: 1
Thread-B-2 执行countB()方法,准备调用countDown()
Thread-B-2 已调用countDown(),剩余计数: 0

在实际项目中,CountDownLatch 是一种强大的同步工具,常用于以下场景:

1. 并行任务协调

多个线程并行执行子任务,主线程需要等待所有子任务完成后再继续执行。
典型场景

  • 批量数据处理:将大任务拆分为多个子任务并行处理,等待所有子任务完成后汇总结果。
  • 系统初始化:多个模块并行初始化,主程序等待所有模块初始化完成后启动服务。

示例代码

CountDownLatch latch = new CountDownLatch(3);// 启动3个线程并行执行任务
for (int i = 0; i < 3; i++) {new Thread(() -> {try {// 执行子任务processTask();} finally {latch.countDown(); // 任务完成,计数减1}}).start();
}// 主线程等待所有子任务完成
latch.await();
System.out.println("所有任务已完成");

2. 资源初始化与依赖等待

确保某些关键资源(如配置文件、数据库连接、网络服务)初始化完成后,其他线程才能继续执行。
典型场景

  • 分布式系统启动:等待所有节点就绪后开始通信。
  • 多服务依赖:微服务架构中,服务 A 依赖服务 B 和 C,需等待 B 和 C 初始化完成。

示例代码

// 主服务等待3个依赖服务初始化
CountDownLatch serviceLatch = new CountDownLatch(3);// 启动3个线程分别初始化服务
new Thread(() -> { initServiceA(); serviceLatch.countDown(); }).start();
new Thread(() -> { initServiceB(); serviceLatch.countDown(); }).start();
new Thread(() -> { initServiceC(); serviceLatch.countDown(); }).start();// 主服务等待所有依赖初始化完成
serviceLatch.await();
startMainService();

3. 性能测试与并发模拟

在多线程性能测试中,确保所有线程同时开始执行,或等待所有线程完成后统计结果。
典型场景

  • 压测工具:模拟大量用户同时访问系统。
  • 并发算法验证:验证多线程环境下的线程安全问题。

示例代码

// 启动门:确保所有线程同时开始
CountDownLatch startGate = new CountDownLatch(1);
// 结束门:统计所有线程完成时间
CountDownLatch endGate = new CountDownLatch(10);// 创建10个工作线程
for (int i = 0; i < 10; i++) {new Thread(() -> {startGate.await(); // 等待统一开始信号try {executeTask();} finally {endGate.countDown();}}).start();
}// 发出开始信号
startGate.countDown();
// 等待所有线程完成
endGate.await();
System.out.println("所有线程执行完毕");

4. 分步任务执行

任务分多个阶段执行,每个阶段需要等待前一阶段所有任务完成。
典型场景

  • 数据处理流水线:解析数据 → 清洗数据 → 存储数据,每个阶段并行处理但需按顺序执行。

示例代码

// 第一阶段完成信号
CountDownLatch phase1Latch = new CountDownLatch(5);
// 第二阶段完成信号
CountDownLatch phase2Latch = new CountDownLatch(5);// 第一阶段:并行解析数据
for (int i = 0; i < 5; i++) {new Thread(() -> {parseData();phase1Latch.countDown();}).start();
}// 等待第一阶段完成
phase1Latch.await();// 第二阶段:并行处理数据
for (int i = 0; i < 5; i++) {new Thread(() -> {processData();phase2Latch.countDown();}).start();
}// 等待第二阶段完成
phase2Latch.await();
System.out.println("所有阶段完成");

5. 替代 join () 方法

与 Thread.join() 相比,CountDownLatch 更灵活:

  • 可在多个线程中调用 countDown()
  • 支持超时等待(await(timeout, unit))。
  • 可重复使用(通过重新创建 CountDownLatch 实例)。

注意事项:

  • 避免重复使用:CountDownLatch 计数为 0 后无法重置,如需循环使用可考虑 CyclicBarrier
  • 异常处理:确保在 finally 块中调用 countDown(),防止任务异常导致计数无法归零。
  • 性能考量:高并发场景下,大量线程等待可能导致上下文切换开销,需谨慎设计。

通过合理使用 CountDownLatch,可有效简化多线程协调逻辑,提升系统并发性能。

http://www.dtcms.com/a/287860.html

相关文章:

  • Git 完全手册:从入门到团队协作实战(2)
  • 万字解析LVS集群
  • Pandas 30分钟
  • Mybatis:注解完成增删改查
  • steam游戏搬砖项目超完整版实操分享
  • 解惑LINQ中的SelectMany用法
  • 48Days-Day03 | 删除公共字符,两个链表的第一个公共结点,mari和shiny
  • CCF编程能力等级认证GESP—C++8级—20250628
  • 【EMC设计基础--信号环路分析、PCB设计规则】
  • 深入解析文件操作(上)- 二进制文件和文本文件,流的概念,文件的打开和关闭
  • Visual Studio Code(VSCode)中设置中文界面
  • 使用C#对象将WinRiver项目文件进行复杂的XML序列化和反序列化实例详解
  • STM32_Hal库学习ADC
  • XSS的反射型、DOM型、存储型漏洞
  • sqli-labs靶场通关笔记:第32-33关 宽字节注入
  • Go语言中的类型转换与类型推断解析
  • Spring AI硬核攻略:235个模块解析 + 6大场景实战
  • view和pure的区别
  • 智能合约代理与批量调用优化:最小代理与MultiCall的应用
  • Python趣味算法:百钱百鸡问题——双重循环优化与算法效率分析
  • 【Bluedroid】btif_av_sink_execute_service之服务器启用源码流程解析
  • Typecho博客Ajax评论功能实现全攻略
  • 我是怎么设计一个防重复提交机制的(库存出库场景)
  • 【C语言进阶】结构体
  • Windows原生环境配置Claude Code MCP(通过JSON)
  • 简单易懂,快表 详解
  • 有趣的算法题:有时针分针秒针的钟表上,一天之内,时针和分针可重合多少次?分别在什么时刻重合?
  • 【Python】Pandas
  • rustdesk客户端编译
  • QT窗口(7)-QColorDiag