当前位置: 首页 > 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.lryc.cn/news/593256.html

相关文章:

  • mac 配置svn
  • XSS原型与原型链
  • Netty集群方案详解与实战(Zookeeper + Redis + RabbitMQ)
  • LangGraph教程9:LangGraph检查点和Send机制
  • 微信小程序171~180
  • 微信小程序入门实例_____从零开始 开发一个“旅行清单 ”微信小程序
  • 微信小程序——世界天气小助手
  • YOLOv11改进 | RFAConv重塑空间注意力助力性能提升
  • 针对大规模语言模型的上下文工程技术调研与总结(翻译并摘要)
  • The Missing Semester of Your CS Education 学习笔记以及一些拓展知识(三)
  • 华为仓颉编程语言语法简介与示例
  • OpenCV 官翻8 - 其他算法
  • 从 Server.xml 到字节码:Tomcat 内核全景与请求旅程 10 000 字深剖
  • 如何解决pip安装报错ModuleNotFoundError: No module named ‘sqlalchemy’问题
  • Jenkins自动化部署.NET应用实战:Docker+私有仓库+SSH远程发布
  • Entity Component System架构
  • 【计算机网络架构】树型架构简介
  • 阶段1--Linux存储管理
  • Azure Bicep 是什么?
  • Vue rem回顾
  • 消息队列与信号量:System V 进程间通信的基础
  • 基于开放API接口采集的定制开发开源AI智能名片S2B2C商城小程序数据整合与增长策略研究
  • C++入门--lesson4
  • react17更新哪些新特性
  • HTTPHTTPSTLSDNSRSA
  • 使用nvm安装node、npm、pnpm以及编译项目教程
  • 编程实现Word自动排版:从理论到实践的全面指南
  • Spring Cloud Gateway与Envoy Sidecar在微服务请求路由中的架构设计分享
  • 第二阶段-第二章—8天Python从入门到精通【itheima】-133节(SQL——DQL——基础查询)
  • 用户中心项目实战(springboot+vue快速开发管理系统)