1、简述
在并发编程中,有时我们需要协调多个线程的执行,确保它们在特定的时刻同步完成。CountDownLatch是Java并发包中提供的一种强大的工具,用于实现这种同步。本文将深入探讨CountDownLatch的基本原理,以及一些实际应用场景中的巧妙应用。
2、什么是 CountDownLatch?
2.1 基本原理
CountDownLatch
位于 java.util.concurrent
包下,它允许一个或多个线程等待,直到在其他线程中执行的一组操作完成。
它通过一个 计数器(count) 来实现:
🔹 每调用一次 countDown()
,计数器减一;
🔹 当计数器减为 0 时,所有因 await()
而阻塞的线程会被释放,继续执行后续逻辑。
简单来说:等待一批任务执行完成后,再统一往下走。
2.2 常见应用场景
🔹 主线程等待多个子线程完成
比如:启动多个线程并行计算,等待所有线程完成后汇总结果。
🔹 模拟并发请求
在性能测试中,可以利用 CountDownLatch
控制多个线程同时开始执行,从而模拟高并发场景。
🔹 分阶段任务控制
有些业务逻辑需要某些步骤都完成之后,才能进入下一阶段。
2.3 使用步骤
🔹 初始化
CountDownLatch latch = new CountDownLatch(N);
N
表示需要等待的事件数量。
🔹 子线程执行任务
任务完成后调用 latch.countDown()
。
🔹 主线程等待
调用 latch.await()
,直到计数器为 0 才继续执行。
3、实践样例
示例 1:等待多个线程完成任务
import java.util.concurrent.CountDownLatch;
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
int threadCount = 3;
CountDownLatch latch = new CountDownLatch(threadCount);
for (int i = 0; i < threadCount; i++) {
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + " 正在执行任务...");
Thread.sleep((long) (Math.random() * 2000));
System.out.println(Thread.currentThread().getName() + " 完成任务");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
latch.countDown(); // 任务完成,计数器减一
}
}).start();
}
System.out.println("主线程等待所有子线程完成任务...");
latch.await(); // 等待所有子线程完成
System.out.println("所有子线程执行完毕,主线程继续");
}
}
输出示例(顺序不固定):
主线程等待所有子线程完成任务...
Thread-0 正在执行任务...
Thread-1 正在执行任务...
Thread-2 正在执行任务...
Thread-1 完成任务
Thread-0 完成任务
Thread-2 完成任务
所有子线程执行完毕,主线程继续
示例 2:模拟并发请求
import java.util.concurrent.CountDownLatch;
public class ConcurrentTest {
public static void main(String[] args) throws InterruptedException {
int clientCount = 5;
CountDownLatch startGate = new CountDownLatch(1); // 控制开始
CountDownLatch endGate = new CountDownLatch(clientCount); // 控制结束
for (int i = 0; i < clientCount; i++) {
new Thread(() -> {
try {
startGate.await(); // 等待一起开始
System.out.println(Thread.currentThread().getName() + " 发起请求");
Thread.sleep((long) (Math.random() * 2000));
System.out.println(Thread.currentThread().getName() + " 请求完成");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
endGate.countDown();
}
}).start();
}
System.out.println("准备就绪,开始并发请求!");
startGate.countDown(); // 所有线程同时开始
endGate.await(); // 等待所有请求完成
System.out.println("所有请求已完成,测试结束");
}
}
4、注意事项
🔹 一次性工具
CountDownLatch
不能重复使用。计数器归零后,就失效了。如果需要循环使用,建议使用 CyclicBarrier
或 Semaphore
。
🔹 countDown() 可在不同线程调用
多个线程调用 countDown()
是线程安全的。
🔹 await() 会阻塞
如果你不希望无限等待,可以使用 await(timeout, unit)
设置超时时间。
5、总结
CountDownLatch是一个在并发编程中非常实用的工具,通过它我们可以轻松实现多个线程之间的同步。在实际应用中,可以根据不同场景灵活运用,用于任务等待、并发线程数量控制等情况。然而,在使用过程中需要注意计数器的递减和重置,以确保线程同步的正确性。通过巧妙应用CountDownLatch,可以使得并发编程更加简单、清晰、可控。
核心方法:
🔹 countDown()
:任务完成后调用。
🔹 await()
:等待所有任务完成后继续执行。
在实际项目中,它常用于 并行任务处理、性能测试、并发模拟。