线程池调优
"线程池参数怎么设?我的服务器是 16 核,应该设多少线程?"
这是被问得最多的问题之一。
答案取决于任务类型。
任务类型决定线程数
CPU 密集型 vs IO 密集型
CPU 密集型:任务主要是计算,极少等待
示例:复杂数学运算、数据加密、图像处理
特点:线程大部分时间在 CPU 上运算
IO 密集型:任务主要是等待 IO
示例:数据库查询、文件读写、HTTP 请求
特点:线程大部分时间在等待线程数计算公式
公式:
线程数 = CPU核心数 * CPU利用率 * (1 + 等待时间/计算时间)对于:
- CPU 密集型:等待时间 ≈ 0,所以线程数 ≈ CPU核心数
- IO 密集型:等待时间 >> 计算时间,所以线程数 > CPU核心数
代码演示
CPU 密集型配置
java
public class CPUIntensiveConfig {
public static void main(String[] args) {
int cpuCores = Runtime.getRuntime().availableProcessors();
System.out.println("CPU 核心数: " + cpuCores);
ThreadPoolExecutor executor = new ThreadPoolExecutor(
cpuCores, // 核心线程数
cpuCores, // 最大线程数
0L, TimeUnit.MILLISECONDS, // 不需要回收
new LinkedBlockingQueue<>(100),
new ThreadPoolExecutor.AbortPolicy()
);
// 提交计算任务
for (int i = 0; i < 20; i++) {
executor.submit(() -> {
long sum = 0;
for (int j = 0; j < 1_000_000; j++) {
sum += Math.sqrt(j);
}
});
}
executor.shutdown();
}
}IO 密集型配置
java
public class IOIntensiveConfig {
public static void main(String[] args) {
int cpuCores = Runtime.getRuntime().availableProcessors();
ThreadPoolExecutor executor = new ThreadPoolExecutor(
cpuCores * 2, // IO 密集型可以多一些
cpuCores * 4,
60L, TimeUnit.SECONDS, // 较长空闲时间
new LinkedBlockingQueue<>(1000),
new ThreadPoolExecutor.AbortPolicy()
);
// 提交 IO 任务
for (int i = 0; i < 50; i++) {
executor.submit(() -> {
try {
Thread.sleep(100); // 模拟 IO 等待
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
executor.shutdown();
}
}混合任务配置
java
public class MixedTaskConfig {
public static void main(String[] args) {
int cpuCores = Runtime.getRuntime().availableProcessors();
// CPU 密集型任务用 CPU 线程池
ExecutorService cpuPool = new ThreadPoolExecutor(
cpuCores, cpuCores,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(100)
);
// IO 密集型任务用 IO 线程池
ExecutorService ioPool = new ThreadPoolExecutor(
cpuCores * 2, cpuCores * 4,
60L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(500)
);
cpuPool.submit(() -> {
long sum = 0;
for (int i = 0; i < 1_000_000; i++) {
sum += Math.sqrt(i);
}
});
ioPool.submit(() -> {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
cpuPool.shutdown();
ioPool.shutdown();
}
}动态调整线程池
java
public class DynamicThreadPool {
private final ThreadPoolExecutor executor;
public DynamicThreadPool() {
this.executor = new ThreadPoolExecutor(
5, 20,
60L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(100)
);
}
public void setCorePoolSize(int size) {
executor.setCorePoolSize(size);
}
public void setMaximumPoolSize(int size) {
executor.setMaximumPoolSize(size);
}
public void prestartAllCoreThreads() {
executor.prestartAllCoreThreads();
}
public void allowCoreThreadTimeOut(boolean allow) {
executor.allowCoreThreadTimeOut(allow);
}
public static void main(String[] args) {
DynamicThreadPool pool = new DynamicThreadPool();
pool.prestartAllCoreThreads(); // 预热
pool.setCorePoolSize(10); // 动态调整
pool.setMaximumPoolSize(30);
pool.allowCoreThreadTimeOut(true); // 允许核心线程超时回收
}
}监控与调优
java
public class ThreadPoolTuningDemo {
public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, 4,
60L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(10)
);
// 定时监控
ScheduledExecutorService monitor = Executors.newScheduledThreadPool(1);
monitor.scheduleAtFixedRate(() -> {
printStats(executor);
}, 0, 2, TimeUnit.SECONDS);
// 提交任务
for (int i = 0; i < 30; i++) {
final int taskId = i;
executor.submit(() -> {
try {
Thread.sleep(500 + new Random().nextInt(500));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
Thread.sleep(10000);
monitor.shutdown();
executor.shutdown();
}
private static void printStats(ThreadPoolExecutor executor) {
System.out.println("=== 线程池状态 ===");
System.out.println("核心线程数: " + executor.getCorePoolSize());
System.out.println("最大线程数: " + executor.getMaximumPoolSize());
System.out.println("当前线程数: " + executor.getPoolSize());
System.out.println("活跃线程数: " + executor.getActiveCount());
System.out.println("已完成任务: " + executor.getCompletedTaskCount());
System.out.println("队列大小: " + executor.getQueue().size());
System.out.println("队列容量: " + executor.getQueue().remainingCapacity());
System.out.println();
}
}经验值参考
| 任务类型 | 线程数公式 | 说明 |
|---|---|---|
| CPU 密集型 | CPU核心数 | CPU核心数 + 1 也常见 |
| IO 密集型 | CPU核心数 * 2 | 或更多,取决于 IO 等待比例 |
| 混合型 | 分开配置 | CPU 任务和 IO 任务用不同线程池 |
注意事项
- 线程数不是越大越好:线程创建、切换、销毁都有开销
- 队列大小要平衡:太小容易拒绝,太大可能 OOM
- 监控是调优的基础:根据实际数据调整
- 考虑任务特性:执行时间长短、阻塞程度都会影响最优配置
- 避免过度优化:先让代码正确运行,再根据数据优化
要点回顾
任务类型 → 线程数公式
CPU 密集型:
线程数 = CPU核心数
队列 = 有界队列
IO 密集型:
线程数 = CPU核心数 * 2(甚至更多)
队列 = 有界队列
keepAliveTime = 较长
混合型:
分开配置,用不同线程池处理不同类型任务