线程池大小需依任务类型(cpu/io/混合型)及下游瓶颈确定:cpu型设为核数+1,io型按qps×耗时估算,混合型应分池隔离;队列须有界,拒绝策略选abortpolicy或callerrunspolicy。

线程池大小不能靠拍脑袋或只看CPU核数,得先分清任务类型,再结合下游瓶颈和运行时表现来定。公式只是起点,不是终点。
CPU密集型:核数+1是安全底线
图像压缩、加密解密、数值计算这类任务几乎不等IO,CPU持续满负荷。线程数超过逻辑核数,只会增加上下文切换开销,反而拖慢吞吐。
- 核心线程数建议设为 Runtime.getRuntime().availableProcessors() + 1,多出的1个用于应对GC暂停或缺页中断,保障调度不卡顿
- maximumPoolSize 通常与 corePoolSize 相同,避免动态扩容引入不确定性
- 队列选有界 LinkedBlockingQueue(如容量100~1000),拒绝策略用 AbortPolicy,让容量不足问题尽早暴露
IO密集型:看等待时间占比,不是翻倍就行
HTTP调用、数据库查询、文件读写,大部分时间在等响应,CPU空闲。关键不是“能开多少线程”,而是“有多少请求正在排队等资源”。
- 理论公式:corePoolSize ≈ CPU核数 × (1 + 平均等待时间 / 平均CPU工作时间)
- 更实用做法:用峰值QPS × 平均IO耗时(秒)估算瞬时并发需求,再加20%~50%缓冲
- 必须检查下游瓶颈:DB连接池最大连接数、HTTP客户端路由连接上限、网卡带宽、服务端限流阈值——线程再多,卡在连接池就全堵住
混合型任务:分池隔离比硬凑一个池更稳
一个接口既做加解密(CPU型)又查三次库(IO型),统一配池必然顾此失彼。CPU型任务可能被IO阻塞线程长期排队,而IO型又因线程不足堆积。
- 优先按主责拆分:计算类走CPU优化池(corePoolSize = 核数+1),远程调用走IO优化池(corePoolSize = 核数×2~3)
- 用 CompletableFuture.supplyAsync(task, executor) 显式指定执行器,不依赖默认线程池
- 若必须共用,保守按IO型配置,但必须监控 getActiveCount() 和 getQueue().size() —— 持续高位说明CPU型任务已在排队饿死
队列和拒绝策略决定真实行为,比线程数还关键
再合理的线程数,配错队列和拒绝策略也白搭。
- LinkedBlockingQueue 无界 → maximumPoolSize 形同虚设,永远只启 corePoolSize 个线程
- SynchronousQueue → 新任务直接触发扩容,直到达 maximumPoolSize,再提交就触发拒绝策略
- 拒绝策略别用默认 DiscardPolicy(静默丢弃),选 AbortPolicy(抛异常) 或 CallerRunsPolicy(由调用线程执行),便于定位容量瓶颈
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











