如何优化ExecutorService
ExecutorService 的优化通常围绕 线程数、任务类型、队列选择、拒绝策略、生命周期管理 等方面展开。下面从实战角度系统讲解优化思路,并给出示例。
一、先明确任务类型(优化前提)
| 任务类型 | 特点 | 推荐模型 |
|---|---|---|
| CPU 密集型 | 计算多、阻塞少 | 线程数 ≈ CPU 核心数 |
| IO 密集型 | 网络、DB、磁盘 | 线程数可以更大 |
| 混合型 | 既有计算又有 IO | 拆分线程池 |
经验公式
- CPU 密集型:
N + 1 - IO 密集型:
N * 2或N / (1 - 阻塞系数)
int cpuNums = Runtime.getRuntime().availableProcessors();
二、合理选择线程池类型
❌ 不推荐
Executors.newCachedThreadPool(); // 无线程数上限,容易 OOM
Executors.newFixedThreadPool(n); // 队列无界,风险高
✅ 推荐:手动创建 ThreadPoolExecutor
ThreadPoolExecutor executor = new ThreadPoolExecutor(
4, // corePoolSize
8, // maximumPoolSize
60L, TimeUnit.SECONDS, // 空闲线程存活时间
new ArrayBlockingQueue<>(100), // 有界队列
new ThreadFactoryBuilder()
.setNameFormat("biz-pool-%d")
.build(),
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
三、队列优化(非常关键)
队列选择建议
| 队列 | 适用场景 | 风险 |
|---|---|---|
ArrayBlockingQueue |
有界、稳定 | ✅ 推荐 |
LinkedBlockingQueue |
默认无界 | ❌ 易 OOM |
SynchronousQueue |
直接交付 | 适合短任务 |
PriorityBlockingQueue |
有优先级 | 逻辑复杂 |
✅ 一定要用有界队列
四、拒绝策略优化
常见策略对比:
| 策略 | 行为 | 建议 |
|---|---|---|
| AbortPolicy | 抛异常 | 默认,不推荐 |
| CallerRunsPolicy | 调用线程执行 | ✅ 推荐 |
| DiscardPolicy | 直接丢弃 | ❌ |
| DiscardOldestPolicy | 丢弃最老 | ❌ |
✅ 自定义拒绝策略(推荐)
new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
// 记录日志 / 降级 / 放入补偿队列
log.warn("task rejected");
}
}
五、线程池拆分(非常重要)
❌ 一个线程池干所有事
✅ 不同业务、不同任务类型拆分线程池
ExecutorService ioExecutor;
ExecutorService cpuExecutor;
ExecutorService asyncLogExecutor;
✅ 优点:
- 避免互相阻塞
- 更容易调优
- 故障隔离
六、生命周期管理(避免泄漏)
正确关闭
executor.shutdown();
if (!executor.awaitTermination(30, TimeUnit.SECONDS)) {
executor.shutdownNow();
}
✅ 禁止:
- 不 shutdown
- 重复创建线程池
七、监控与调优(生产必备)
关键指标
executor.getActiveCount();
executor.getQueue().size();
executor.getCompletedTaskCount();
✅ 结合:
- 日志
- Prometheus / Micrometer
八、CompletableFuture + 线程池(现代写法)
CompletableFuture.supplyAsync(() -> doWork(), executor)
.thenAccept(result -> handle(result));
✅ 优点:
- 异步编排
- 异常可控
- 可读性高
九、常见错误总结
❌ 使用 Executors 默认方法
❌ 队列无界
❌ 线程数过大
❌ 一个线程池通吃所有任务
❌ 不监控、不关闭
十、推荐配置模板
ThreadPoolExecutor pool = new ThreadPoolExecutor(
cpuNums * 2,
cpuNums * 4,
30,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(200),
new ThreadFactoryBuilder().setNameFormat("io-pool-%d").build(),
new ThreadPoolExecutor.CallerRunsPolicy()
);
如果你愿意,可以告诉我:
- 任务类型(IO / CPU / 混合)
- QPS / 并发量
- 是否 Web / 后台任务
我可以帮你 直接给出最优线程池参数配置。