避免线程耗尽的关键是匹配业务负载:核心线程数按压测估算(如50×(1+300/50)=350),队列须有界并监控使用率,拒绝策略优先abortpolicy告警,最大线程数i/o型设为core×2~3且keepalivetime≥60秒。

避免应用服务器线程耗尽,关键不是堆砌更多线程,而是让线程池行为与真实业务负载匹配——尤其要防住“看似空闲、实则卡死”的隐性耗尽。
核心线程数别硬套CPU核数
很多团队直接用 Runtime.getRuntime().availableProcessors() 设置 corePoolSize,结果在 I/O 密集型服务(如 HTTP 调用、数据库查询)中严重欠配。这类任务大量时间花在等待响应上,线程实际 CPU 占用很低。建议按压测结果反推:观察平均并发请求数 + 任务平均等待时长,用公式 corePoolSize ≈ 平均并发 × (1 + 平均等待时间 / 平均计算时间) 估算。例如平均同时有 50 个请求,每个请求等待 DB 300ms、计算 50ms,则合理值约 50 × (1 + 300/50) = 350,而非 4 或 8。
队列必须有界,且容量要可监控
用 LinkedBlockingQueue 默认无界队列是高危操作——任务持续堆积,内存涨到 OOM,线程池却始终不扩容、不拒绝,表面运行正常,实则已丧失响应能力。应明确设限:ArrayBlockingQueue(200) 或 SynchronousQueue(适合任务提交速率与处理速率基本持平的场景)。同时把队列剩余容量暴露为监控指标,当使用率持续 >70%,说明线程或队列配置已跟不上流量节奏。
拒绝策略不能只用 CallerRunsPolicy
CallerRunsPolicy 让调用方线程执行任务,虽能减缓提交速度,但会阻塞业务线程(如 Tomcat 的 worker 线程),导致请求线程池整体雪崩。生产环境推荐组合策略:
- 优先用 AbortPolicy 配合告警——抛异常即触发监控告警,快速定位瓶颈
- 对非核心任务,可自定义拒绝逻辑:记录日志 + 降级返回默认值
- 绝不允许拒绝策略静默丢弃(DiscardPolicy)或丢老任务(DiscardOldestPolicy),这会让问题难以追溯
最大线程数要留安全余量
maximumPoolSize 不是“越大越好”。超过一定阈值后,线程上下文切换开销会急剧上升,吞吐反而下降。经验法则是:I/O 密集型任务,max ≤ core × 2~3;CPU 密集型严格控制在 core + 1~2。更重要的是——配合 keepAliveTime 设为 60 秒以上,让突发流量退去后,多余线程能平稳回收,避免长期维持高线程数占用资源。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











