关键字
Java、多线程、并发、线程池、CountDownLatch
背景环境
问题场景
- 需要上传数千个文件到文件服务器
- 单个文件上传耗时约30秒
- 串行上传总耗时过长
- 需要实现并发上传以提高效率
原因分析
核心问题
- 并发控制
- 需要确保多个线程能够安全地处理不同文件
- 需要解决线程间的同步问题
- 结果收集
- 需要记录每个文件的上传结果(成功/失败)
- 资源管理
- 需要合理管理线程资源,避免资源耗尽
解决过程
方案探索
尝试方案 | 实现方式 | 问题分析 |
---|---|---|
Runnable接口 | 实现Runnable接口创建线程 | run方法无返回值,无法直接获取结果 |
Thread继承 | 继承Thread类创建线程 | 代码侵入性较强,不够灵活 |
线程池+CountDownLatch | 使用线程池管理,CountDownLatch同步 | 最终采用方案,灵活高效 |
最终解决
标准化解决方案
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.List;
import java.util.ArrayList;
public class ConcurrencyResultExample {
public static void main(String[] args) throws InterruptedException {
int poolSize = 2;
int numberOfThreads = 2;
// 创建线程池
ExecutorService executor = Executors.newFixedThreadPool(poolSize);
// 创建同步计数器
CountDownLatch latch = new CountDownLatch(numberOfThreads);
List<String> results = new ArrayList<>();
for (int i = 0; i < numberOfThreads; i++) {
final int index = i;
executor.execute(() -> {
try {
// 执行任务
String result = "Result from Thread " + index;
Thread.sleep(2000); // 模拟操作
// 线程安全地添加结果
synchronized (results) {
results.add(result);
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
// 确保计数器递减
latch.countDown();
}
});
}
// 等待所有线程完成
latch.await();
// 输出结果
for (String result : results) {
System.out.println(result);
}
executor.shutdown();
}
}
关键点说明
- 线程池管理
- 使用
Executors.newFixedThreadPool
创建固定大小的线程池 - 有效控制并发线程数量,避免资源耗尽
- 使用
- 结果收集
- 使用
synchronized
块确保线程安全地操作共享结果集
- 使用
- 同步控制
- 使用
CountDownLatch
实现线程同步 - 确保主线程在所有任务完成后继续执行
- 使用
总结
- 并发实践
- Java并发编程需要特别注意线程安全和资源管理
- 合理使用线程池可以显著提高程序性能
- 工具选择
CountDownLatch
是处理并发任务同步的有效工具- 线程池比直接创建线程更高效、更安全
- 扩展思考
- 可以将该模式封装为通用工具类
- 适用于批量文件处理、数据导入等场景