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,可有效简化多线程协调逻辑,提升系统并发性能。

相关推荐
Swift社区1 分钟前
LeetCode 453 - 最小操作次数使数组元素相等
算法·leetcode·职场和发展
千寻技术帮2 分钟前
10370_基于Springboot的校园志愿者管理系统
java·spring boot·后端·毕业设计
Rinai_R2 分钟前
关于 Go 的内存管理这档事
java·开发语言·golang
咸鱼加辣3 分钟前
【python面试】你x的启动?
开发语言·python
聆风吟º4 分钟前
【Spring Boot 报错已解决】彻底解决 “Main method not found in class com.xxx.Application” 报错
java·spring boot·后端
hoiii1876 分钟前
LR算法辅助的MIMO系统Zero Forcing检测
算法
木易 士心6 分钟前
数字身份的通行证:深入解析单点登录(SSO)的架构与艺术
java·大数据·架构
糖葫芦君8 分钟前
Lora模型微调
人工智能·算法
tyatyatya11 分钟前
MATLAB图形交互教程:鼠标拾取/坐标轴交互/动态绘图实战详解
开发语言·matlab·计算机外设
乐茵lin12 分钟前
golang中 Context的四大用法
开发语言·后端·学习·golang·编程·大学生·context