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

java 复制代码
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类实例,初始计数为3
        A 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 () 调用完成
    • 输出结果显示等待是否成功

运行结果示例:

java 复制代码
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()已调用
主线程继续执行...

或者(如果超时):

java 复制代码
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. 并行任务协调

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

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

示例代码

java 复制代码
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 初始化完成。

示例代码

java 复制代码
// 主服务等待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. 性能测试与并发模拟

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

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

示例代码

java 复制代码
// 启动门:确保所有线程同时开始
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. 分步任务执行

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

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

示例代码

java 复制代码
// 第一阶段完成信号
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,可有效简化多线程协调逻辑,提升系统并发性能。

相关推荐
瓜子三百克42 分钟前
Swift6.1 - 基础知识1: 简单值、控制流、函数和闭包
开发语言·swift
froginwe1144 分钟前
Swift 条件语句
开发语言
2301_794461571 小时前
牛客-倒置字符串
算法·leetcode·动态规划
COSMOS_*1 小时前
2025最新版 Go语言&Goland 专业安装及配置(超详细)
开发语言·后端·golang·go
贰拾wan1 小时前
Redis的持久化-RDB
java·数据库·redis·缓存
云青山水林2 小时前
板子 5.29--7.19
数据结构·算法
Dcs2 小时前
超600个Laravel应用遭远程执行漏洞攻击:罪魁祸首是GitHub泄露的APP_KEY!
java
ChinaRainbowSea2 小时前
用户中心——比如:腾讯的QQ账号可以登录到很多应用当中 02
java·服务器·spring boot·后端·mysql·mybatis
Kiri霧2 小时前
Kotlin方差
android·开发语言·kotlin
子豪-中国机器人3 小时前
2025年7月19日,二维矩阵
数据结构·c++·算法