在Java并发编程中,线程池是最重要的工具之一。合理使用线程池可以显著提升程序性能,降低资源消耗。本文将深入讲解Java线程池的7个典型使用场景,并附完整代码示例。
一、线程池基础回顾
Java通过java.util.concurrent.ExecutorService
接口提供线程池功能,最常用的实现是ThreadPoolExecutor
。创建线程池的推荐方式是使用Executors
工厂类,但理解核心参数至关重要:
- corePoolSize:核心线程数
- maximumPoolSize:最大线程数
- keepAliveTime:线程空闲时间
- workQueue:任务队列
- threadFactory:线程工厂
- handler:拒绝策略
二、7个线程池实战案例
案例1:固定大小线程池处理IO密集型任务
ExecutorService executor = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
executor.submit(() -> {
// 模拟IO操作
try {
Thread.sleep(1000);
System.out.println("处理完成:" + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
executor.shutdown();
案例2:可缓存线程池处理CPU密集型任务
ExecutorService executor = Executors.newCachedThreadPool();
for (int i = 0; i < 100; i++) {
final int taskId = i;
executor.execute(() -> {
// 复杂计算
System.out.println("计算任务" + taskId + "由" + Thread.currentThread().getName() + "执行");
});
}
案例3:定时任务线程池
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
// 延迟执行
scheduler.schedule(() -> System.out.println("延迟5秒执行"), 5, TimeUnit.SECONDS);
// 周期性执行
scheduler.scheduleAtFixedRate(() -> System.out.println("每3秒执行一次"), 1, 3, TimeUnit.SECONDS);
案例4:自定义线程池参数
ThreadPoolExecutor customExecutor = new ThreadPoolExecutor(
2, // 核心线程数
10, // 最大线程数
60, TimeUnit.SECONDS, // 空闲时间
new ArrayBlockingQueue<>(100), // 任务队列
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
案例5:获取任务执行结果
ExecutorService executor = Executors.newFixedThreadPool(3);
List<Future<Integer>> results = new ArrayList<>();
for (int i = 0; i < 5; i++) {
final int num = i;
Future<Integer> future = executor.submit(() -> {
Thread.sleep(1000);
return num * num;
});
results.add(future);
}
for (Future<Integer> f : results) {
System.out.println("结果:" + f.get());
}
案例6:优雅关闭线程池
ExecutorService executor = Executors.newFixedThreadPool(3);
// 提交任务...
executor.shutdown(); // 不再接受新任务
try {
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
executor.shutdownNow(); // 强制终止
}
} catch (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
案例7:监控线程池状态
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(3);
// 定期监控
ScheduledExecutorService monitor = Executors.newSingleThreadScheduledExecutor();
monitor.scheduleAtFixedRate(() -> {
System.out.println("活跃线程数:" + executor.getActiveCount());
System.out.println("已完成任务数:" + executor.getCompletedTaskCount());
System.out.println("队列大小:" + executor.getQueue().size());
}, 0, 1, TimeUnit.SECONDS);
三、线程池优化技巧
- 合理设置线程数:
- CPU密集型:核心线程数 = CPU核数 + 1
-
IO密集型:核心线程数 = CPU核数 × (1 + 平均等待时间/平均计算时间)
-
选择合适的队列:
- 无界队列(LinkedBlockingQueue):可能导致OOM
-
有界队列(ArrayBlockingQueue):更安全
-
自定义拒绝策略:
- 记录日志
- 持久化任务
-
降级处理
-
线程命名:通过自定义ThreadFactory给线程命名,便于排查问题
-
异常处理:为任务添加统一的异常处理逻辑
四、常见问题解答
Q:线程池中的线程会一直存活吗?
A:核心线程默认会一直存活,非核心线程在空闲超过keepAliveTime后会被回收。
Q:如何选择线程池类型?
A:FixedThreadPool适合负载较重的服务器,CachedThreadPool适合短期异步任务。
Q:为什么推荐自定义ThreadPoolExecutor?
A:Executors提供的工厂方法隐藏了参数细节,可能引发OOM等问题。
通过本文的7个实战案例,你应该已经掌握了Java线程池的核心用法。记住,线程池调优需要根据实际场景进行测试和调整,没有放之四海而皆准的最优配置。
版权声明
本文仅代表作者观点,不代表百度立场。
本文系作者授权百度百家发表,未经许可,不得转载。