CountDownLatch是Java并发包中提供的同步辅助类,主要用于让一个或多个线程等待其他线程完成一系列操作后再继续执行,在多线程任务拆分、并行处理等场景中应用十分广泛。

CountDownLatch核心原理
CountDownLatch内部维护了一个计数器,创建实例时需要指定计数器的初始值,这个值通常对应需要等待的子任务数量。它提供了两个核心方法:
countDown():每个子任务执行完成后调用该方法,计数器值减一await():需要等待的线程调用该方法,会阻塞直到计数器值归零
当计数器的值减到0时,所有因为调用await()方法而阻塞的线程都会被唤醒,继续执行后续逻辑。需要注意的是,CountDownLatch的计数器是一次性的,归零之后无法重置,无法重复使用。
基础使用示例
下面通过一个简单的示例展示CountDownLatch的基本用法,场景是主线程等待3个子线程全部执行完成后再输出汇总结果:
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CountDownLatchBasicDemo {
public static void main(String[] args) throws InterruptedException {
// 初始化计数器,值为3,对应3个子任务
CountDownLatch latch = new CountDownLatch(3);
ExecutorService executor = Executors.newFixedThreadPool(3);
for (int i = 0; i < 3; i++) {
int taskId = i;
executor.submit(() -> {
try {
System.out.println("子任务" + taskId + "开始执行");
// 模拟子任务执行耗时
Thread.sleep(1000);
System.out.println("子任务" + taskId + "执行完成");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 任务完成后计数器减一
latch.countDown();
}
});
}
// 主线程等待,直到计数器归零
latch.await();
System.out.println("所有子任务执行完成,主线程继续执行汇总逻辑");
executor.shutdown();
}
}
常见实战场景
场景一:并行初始化多个资源
在应用启动阶段,经常需要并行初始化多个独立的资源,比如加载配置文件、建立数据库连接、初始化缓存等,所有资源初始化完成后再启动服务。使用CountDownLatch可以实现这个需求:
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ResourceInitDemo {
public static void main(String[] args) throws InterruptedException {
CountDownLatch initLatch = new CountDownLatch(3);
ExecutorService executor = Executors.newFixedThreadPool(3);
// 初始化配置资源
executor.submit(() -> {
try {
System.out.println("开始加载配置文件");
Thread.sleep(800);
System.out.println("配置文件加载完成");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
initLatch.countDown();
}
});
// 初始化数据库连接
executor.submit(() -> {
try {
System.out.println("开始建立数据库连接");
Thread.sleep(1200);
System.out.println("数据库连接建立完成");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
initLatch.countDown();
}
});
// 初始化缓存
executor.submit(() -> {
try {
System.out.println("开始初始化缓存");
Thread.sleep(600);
System.out.println("缓存初始化完成");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
initLatch.countDown();
}
});
// 等待所有资源初始化完成
initLatch.await();
System.out.println("所有资源初始化完成,服务开始启动");
executor.shutdown();
}
}
场景二:拆分大任务并行处理
当需要处理一个大型计算任务时,可以将任务拆分成多个子任务分配给不同线程并行处理,所有子任务处理完成后再合并结果。比如统计一个大文件中不同关键词的出现次数:
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Map;
public class TaskSplitDemo {
public static void main(String[] args) throws InterruptedException {
// 模拟拆分后的4个子任务
int taskCount = 4;
CountDownLatch taskLatch = new CountDownLatch(taskCount);
// 存储每个子任务的处理结果
Map<String, Integer> resultMap = new ConcurrentHashMap<>();
ExecutorService executor = Executors.newFixedThreadPool(taskCount);
for (int i = 0; i < taskCount; i++) {
int partId = i;
executor.submit(() -> {
try {
System.out.println("处理第" + partId + "部分数据");
// 模拟数据处理
Thread.sleep(1000);
// 模拟每个部分的处理结果
resultMap.put("part_" + partId, partId * 10);
System.out.println("第" + partId + "部分数据处理完成");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
taskLatch.countDown();
}
});
}
// 等待所有子任务完成
taskLatch.await();
// 合并结果
int total = resultMap.values().stream().mapToInt(Integer::intValue).sum();
System.out.println("所有子任务处理完成,合并结果总值为:" + total);
executor.shutdown();
}
}
注意事项
- 一定要在
finally块中调用countDown()方法,避免子任务执行异常导致计数器无法减一,造成主线程永久阻塞 await()方法提供了带超时时间的重载版本await(long timeout, TimeUnit unit),可以避免无限等待,超时后主线程会继续执行- CountDownLatch无法重复使用,如果需要可重复使用的计数器,可以考虑使用CyclicBarrier
- 不要在同一个线程中多次调用
countDown()方法,除非你明确知道计数器的初始值设置是正确的
和其他同步工具对比
| 工具类 | 核心特点 | 适用场景 |
|---|---|---|
| CountDownLatch | 一次性计数器,等待其他线程完成 | 等待多个子任务完成后再执行后续逻辑 |
| CyclicBarrier | 可循环使用,等待所有线程到达屏障点 | 多个线程相互等待,都到达后再同时继续执行 |
| Semaphore | 控制同时访问资源的线程数量 | 限流、资源池访问控制 |
CountDownLatch多线程同步Java并发线程等待修改时间:2026-07-23 16:09:35