本文目录导读:

CountDownLatch 是 Java 中一个非常实用的并发工具类,它允许一个或多个线程等待其他线程完成一组操作后再继续执行。
它的核心思想就像一个倒计数器:初始化一个计数,每当一个线程完成了它的任务,就调用 countDown() 方法让计数器减1,等待的线程会调用 await() 方法阻塞,直到计数器变为0。
核心概念
- 初始计数:在创建
CountDownLatch对象时设定,表示需要等待完成的事件数量。 countDown():事件完成时调用,计数器减1。await():调用此方法的线程会被阻塞,直到计数器的值为0。- 一次性:计数器的值一旦降到0,就无法被重置,如果需要重置,请使用
CyclicBarrier。
典型应用场景
- 主线程等待多个子线程完成:主线程启动N个线程执行任务,然后主线程等待所有子线程完成后再继续。
- 并行处理:将一个大的任务拆分成多个子任务,让多个线程并行执行,最后等待所有子任务结果汇总。
- 模拟高并发:用
CountDownLatch控制多个线程同时开始执行某个操作。
代码示例
场景 1:主线程等待所有子线程完成
这是最常见的用法,主线程作为等待者,子线程作为完成者。
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
// 1. 初始化计数器为 5,表示有 5 个任务要完成
CountDownLatch latch = new CountDownLatch(5);
System.out.println("主线程开始,等待 " + latch.getCount() + " 个线程完成...");
// 2. 启动 5 个子线程
for (int i = 1; i <= 5; i++) {
new Thread(new Worker(latch, "线程-" + i)).start();
}
// 3. 主线程调用 await(),在此处阻塞,直到计数器为 0
latch.await();
// 或者带超时时间的等待:latch.await(3, TimeUnit.SECONDS);
System.out.println("主线程:所有子线程已完成,继续执行后续逻辑。");
}
}
// 工作线程,模拟执行任务
class Worker implements Runnable {
private final CountDownLatch latch;
private final String name;
public Worker(CountDownLatch latch, String name) {
this.latch = latch;
this.name = name;
}
@Override
public void run() {
try {
System.out.println(name + " 开始工作...");
Thread.sleep((long) (Math.random() * 2000)); // 模拟随机耗时工作
System.out.println(name + " 工作完成。");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 4. 重要:任务完成后,调用 countDown() 使计数器减 1
// 务必放在 finally 块中,避免因异常导致计数器永远无法归零
latch.countDown();
}
}
}
输出示例:
主线程开始,等待 5 个线程完成...
线程-1 开始工作...
线程-2 开始工作...
线程-4 开始工作...
线程-3 开始工作...
线程-5 开始工作...
线程-5 工作完成。
线程-2 工作完成。
线程-4 工作完成。
线程-1 工作完成。
线程-3 工作完成。
主线程:所有子线程已完成,继续执行后续逻辑。
场景 2:模拟高并发(所有线程一起开始)
这里使用两个 CountDownLatch:
startLatch(初始计数 = 1):用于让所有工作线程在同一起跑线上等待。endLatch(初始计数 = 5):用于让主线程等待所有工作线程执行完毕。
import java.util.concurrent.CountDownLatch;
public class ConcurrentStartDemo {
public static void main(String[] args) throws InterruptedException {
int threadCount = 5;
// 用于控制所有线程同时开始的阀门
CountDownLatch startLatch = new CountDownLatch(1);
// 用于主线程等待所有子线程结束
CountDownLatch endLatch = new CountDownLatch(threadCount);
for (int i = 0; i < threadCount; i++) {
int finalI = i;
new Thread(() -> {
try {
// 1. 所有线程启动后,都阻塞在 startLatch.await() 这里
startLatch.await();
// 2. 当 startLatch 计数为 0 时,所有线程几乎同时开始执行
System.out.println("线程 " + Thread.currentThread().getName() + " 开始执行任务 " + finalI);
Thread.sleep((long) (Math.random() * 1000));
System.out.println("线程 " + Thread.currentThread().getName() + " 执行完毕");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 3. 每个线程执行完毕后,让 endLatch 减 1
endLatch.countDown();
}
}, "Thread-" + i).start();
}
System.out.println("主线程准备发令...");
Thread.sleep(2000); // 模拟准备工作
System.out.println("主线程:预备——跑!");
// 4. 将 startLatch 减到 0,所有等待的工作线程同时开始
startLatch.countDown();
// 5. 主线程等待所有工作线程结束
endLatch.await();
System.out.println("主线程:所有线程执行完毕,程序结束。");
}
}
关键注意事项
- 计数器无法重置:
CountDownLatch是一次性的,计数归零后不能再使用。 countDown()放在finally块中:确保无论任务是否抛出异常,计数器都能正确减1,避免死锁或程序永远等待。- 避免
await()线程无限等待:可以使用带超时参数的await(long timeout, TimeUnit unit),防止因子线程异常或未正确调用countDown()导致主线程永远阻塞。 getCount()实时查询:该方法可以查询当前剩余计数,但返回的值是瞬时的,在多线程环境下不具备严格的同步语义。
与 CyclicBarrier 的区别
| 特性 | CountDownLatch |
CyclicBarrier |
|---|---|---|
| 可重用性 | 一次性,计数归零后不可用 | 可循环使用(通过 reset() 或自动重置) |
| 角色 | 等待者(一个或多个线程)等待完成者(多个线程)完成任务 | 所有线程相互等待,直到所有线程都到达一个“屏障点” |
| 核心方法 | countDown()、await() |
await() |
| 计数器位置 | 在等待者(如主线程)手中 | 在工作线程中 |
| 典型场景 | 一个线程等待多个线程完成 | 多个线程相互等待,到达某个点后继续或执行屏障动作 |
一句话总结:
CountDownLatch 是 “一个人等一群人的结果”;
CyclicBarrier 是 “一群人互相等,人到齐了一起走”。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播