Java并发工具:CountDownLatch
CountDownLatch是Java并发包(java.util.concurrent)中提供的一种同步工具,用于控制一个或多个线程等待其他线程完成操作。它是一个非常有用的工具类,常用于协调多个线程之间的执行顺序。
基本概念
CountDownLatch 内部维护了一个计数器,这个计数器表示需要等待的事件数量:
- 调用 countDown() 方法会将计数器减 1。
- 调用 await() 方法的线程会一直阻塞,直到计数器变为 0。
常用方法
方法名 | 描述 |
CountDownLatch(int count) | 构造函数,初始化计数器为 count |
void await() | 当前线程等待,直到计数器变为 0 |
boolean await(long timeout, TimeUnit unit) | 等待一段时间后超时返回,避免永久阻塞 |
void countDown() | 计数器减 1 |
long getCount() | 获取当前计数器的值(调测程序用) |
实现原理
- 基于AQS共享模式
内部通过继承
AbstractQueuedSynchronizer(AQS)的Sync类实现,使用state变量表示当前计数值。
- 原子操作保证线程安全
countDown()方法通过CAS(Compare-and-Swap)操作原子性减少计数器值。
- 一次性特性
计数器归零后无法重置,若需重复使用同步机制,应选择CyclicBarrier。
使用场景
场景一:主线程等待所有子线程完成任务后再继续执行
import java.util.concurrent.CountDownLatch;
public class LatchExample {
public static void main(String[] args) throws InterruptedException {
int numThreads = 5;
CountDownLatch latch = new CountDownLatch(numThreads);
for (int i = 1; i <= numThreads; i++) {
final int threadNum = i;
new Thread(() -> {
try {
System.out.println("Thread " + threadNum + " is working...");
Thread.sleep(1000); // 模拟工作
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
latch.countDown(); // 完成后减少计数器
}
}).start();
}
latch.await(); // 主线程等待所有线程完成
System.out.println("All threads have finished. Main thread continues.");
}
}
执行结果
Thread 3 is working...
Thread 5 is working...
Thread 2 is working...
Thread 1 is working...
Thread 4 is working...
All threads have finished. Main thread continues.
场景二:多个线程等待一个启动信号
比如多个线程准备就绪后同时开始执行:
import java.util.concurrent.CountDownLatch;
public class StartTogetherExample {
public static void main(String[] args) {
CountDownLatch startSignal = new CountDownLatch(1);
for (int i = 1; i <= 3; i++) {
final int threadNum = i;
new Thread(() -> {
try {
System.out.println("Thread " + threadNum + " is ready.");
startSignal.await(); // 所有线程等待启动信号
System.out.println("Thread " + threadNum + " starts working.");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
}
try {
Thread.sleep(2000); // 主线程模拟延迟后发出启动信号
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
startSignal.countDown(); // 启动所有线程
}
}
执行结果
Thread 3 is ready.
Thread 1 is ready.
Thread 2 is ready.
Thread 3 starts working.
Thread 1 starts working.
Thread 2 starts working.
注意事项
- CountDownLatch是一次性使用的,一旦计数器减到 0,就不能再重置。
-如果你需要重复使用的类似功能,请考虑使用 CyclicBarrier。
- await() 方法可以被中断,建议捕获 InterruptedException 并做适当处理。
-避免资源泄漏:确保countDown()在finally块中调用,防止任务异常终止导致计数器未递减。
-超时机制:可结合await(long timeout, TimeUnit unit)设置超时时间,避免无限等待。
小结
功能 | 类比 |
多个线程等待某个条件满足后一起执行 | 运动员等裁判发令枪响才起跑 |
主线程等待多个子线程完成再继续 | 老师等所有学生交卷才能统计成绩 |