关于Java线程池
Java 线程池参数设置的依据
以 ThreadPoolExecutor 的核心构造参数为例:
public ThreadPoolExecutor(
int corePoolSize, // 核心线程数
int maximumPoolSize, // 最大线程数
long keepAliveTime, // 非核心线程空闲存活时间
TimeUnit unit, // 存活时间单位
BlockingQueue<Runnable> workQueue, // 任务队列
ThreadFactory threadFactory, // 线程工厂
RejectedExecutionHandler handler // 拒绝策略
)
1. corePoolSize(核心线程数)
依据:任务类型 + CPU 核数
CPU 密集型任务(计算、加密、压缩等)
corePoolSize = CPU 逻辑核数 + 1
- 原因:CPU 密集型任务一直占用 CPU,线程数超过核数反而会增加上下文切换开销
- +1 的原因:当偶尔某个线程因缺页中断等原因暂停时,多出的那个线程可以顶上,避免 CPU 空转
IO 密集型任务(网络请求、数据库、文件读写等)
corePoolSize = CPU 逻辑核数 × 2
// 或者更精确地:
corePoolSize = CPU 逻辑核数 × (1 + IO 等待时间 / CPU 计算时间)
- 原因:IO 操作期间线程不占用 CPU(处于 WAITING/TIMED_WAITING 状态),需要更多线程来填补等待空隙
- IO 等待比例越高,需要的线程越多
混合型任务
corePoolSize = CPU 逻辑核数 × (1 + IO 等待时间 / CPU 计算时间)
- 关键是估算 CPU 计算时间与 IO 等待时间的比例
- 可以通过 Profiling 工具(如 Arthas、JFR)观测
2. maximumPoolSize(最大线程数)
依据:系统资源上限 + 任务突发量
|
场景 |
推荐设置 |
依据 |
|
CPU 密集型 |
= corePoolSize |
线程数过多无收益,反而增加调度开销 |
|
IO 密集型 |
= corePoolSize × (1.5 ~ 2) |
应对突发流量,留出弹性空间 |
|
有明确 SLA 要求 |
根据压测结果反推 |
保证在最大负载下仍满足响应时间要求 |
核心原则:最大线程数不是越大越好。线程数过多 → 内存占用高(每个线程约 1MB 栈空间)→ 上下文切换频繁 → 性能下降
3. keepAliveTime + unit(空闲存活时间)
依据:流量波动特征
|
场景 |
推荐设置 |
依据 |
|
流量波动大 |
60秒 ~ 120秒 |
突发后快速回收闲置线程,释放资源 |
|
流量较平稳 |
300秒 ~ 600秒 |
避免频繁创建/销毁线程 |
|
极端节省资源 |
30秒 |
尽快回收,但不至于太频繁创建 |
- 核心线程默认不会回收,如需回收可设置:
executor.allowCoreThreadTimeOut(true) - 需要权衡:时间太短 → 突发来时频繁创建线程;时间太长 → 闲置线程浪费内存
4. workQueue(任务队列)
依据:任务特征 + 内存约束 + 延迟要求
|
队列类型 |
特点 |
适用场景 |
|
|
队列无限大,maximumPoolSize 基本失效 |
不推荐,可能导致 OOM |
|
|
队列有上限,超出后创建非核心线程 |
IO 密集型,允许一定缓冲 |
|
|
有界,公平/非公平可选 |
需要严格控制内存时 |
|
|
不存储任务,直接交给线程 |
CPU 密集型或低延迟要求,任务不能排队 |
|
|
按优先级排序 |
任务有优先级区分时 |
队列长度依据:
队列长度 ≈ 每秒任务 arrivals × 可接受的最大等待时间
例如:每秒 1000 个请求,可接受最大等待 2 秒 → 队列长度 ≈ 2000
5. handler(拒绝策略)
依据:业务语义
|
策略 |
行为 |
适用场景 |
|
|
抛 RejectedExecutionException |
需要感知丢弃,及时告警 |
|
|
由提交任务的线程自己执行 |
不想丢弃任务,且可接受降速 |
|
|
静默丢弃 |
可容忍数据丢失(如日志采集) |
|
|
丢弃队列最老的任务 |
新任务优先级更高 |
|
自定义 |
记录日志 + 持久化 + 告警 |
生产环境推荐 |
实际生产中的推荐做法
第一步:用公式算初值
int cores = Runtime.getRuntime().availableProcessors();
// CPU 密集型
int coreSize = cores + 1;
int maxSize = cores + 1;
// IO 密集型
int coreSize = cores * 2;
int maxSize = cores * 4;
第二步:压测验证
- 逐步加压,观察 吞吐量(TPS)、响应时间(RT)、CPU 利用率
- 找到性能拐点,在拐点附近取值
第三步:运行时监控调整
- 监控线程池活跃度、队列堆积、拒绝次数
- 根据线上实际表现再微调
简明速查表
CPU 密集型 IO 密集型
corePoolSize 核数+1 核数×2 ~ 核数×(1+W/C)
maximumPoolSize = corePoolSize corePoolSize × 1.5~2
keepAliveTime 60s 60s~120s
workQueue SynchronousQueue LinkedBlockingQueue(有界)
拒绝策略 CallerRunsPolicy CallerRunsPolicy + 告警
W/C = IO 等待时间 / CPU 计算时间,可通过性能分析工具测定
最关键的一点:公式只是起点,压测才是依据。任何不结合实际负载和压测数据的参数设定都是不靠谱的。

浙公网安备 33010602011771号