abortpolicy最危险,因超载直接抛rejectedexecutionexception导致线程崩溃和任务静默丢失;需配套try-catch、上下文日志与监控打点。

AbortPolicy 为什么默认却最危险?
它不是“默认所以安全”,而是“默认所以容易出事”——任务一超载就抛 RejectedExecutionException,不捕获就直接崩线程、中断调用链。
- 常见错误现象:日志里突然出现大量
RejectedExecutionException,但业务没告警、没重试,订单/消息静默丢失 - 适用场景:你有完善的异常捕获+监控上报+人工介入流程;或者这是个离线批处理任务,失败可接受且能后续补跑
- 必须做的三件事:
try-catch包住execute();记录任务关键字段(如ID、类型);打点到监控系统(如metrics.increment("threadpool.rejected")) - 坑:很多人只 catch 了,但没 log 任务上下文,排查时根本不知道哪个请求被拒了
CallerRunsPolicy 真的“不丢任务”吗?
它确实把任务塞回调用线程执行,表面看“一个没丢”,但代价是调用线程被拖住——如果调用方是 Web 请求线程(比如 Tomcat 的 http-nio-8080-exec-5),那这个请求就会卡死,响应时间飙升甚至超时。
- 适用场景:后台定时任务调度器、低并发内部服务、或你明确控制了提交节奏(比如每秒最多 10 次 submit)
- 参数差异:它对
corePoolSize和队列大小不敏感,但极度依赖调用线程本身的吞吐能力 - 性能影响:高并发下会反向压垮上游,把线程池压力传导成 HTTP 超时或 RPC 失败,监控上看是“上游慢”,实际根因在下游线程池策略
- 建议搭配:加
if (executor.isShutdown())预检,避免 shutdown 后还往死里塞任务
DiscardPolicy 和 DiscardOldestPolicy 到底丢谁?
DiscardPolicy 是真·静音丢弃——连日志都不打;DiscardOldestPolicy 表面“智能”,实则危险:它先从队列头 poll 一个任务(也就是最老的待执行任务),再尝试重新 execute() 新任务——但这个“重试”仍可能再次失败,而老任务已永久消失。
- 常见错误现象:用
DiscardOldestPolicy后发现关键任务总被丢,查日志发现丢的是刚进队列的“高频心跳”任务,结果真正该执行的“结算单”反而卡在队尾等不到机会 - 使用场景:
DiscardPolicy仅适合日志采集、埋点上报等完全可丢的流量;DiscardOldestPolicy仅适合 FIFO 不重要的场景(如实时行情快照更新) - 坑:两者都无反馈,线上无法感知丢弃行为;若没配
ThreadPoolExecutor.DiscardPolicy的包装类做日志埋点,等于盲操作
怎么选?先看队列类型和业务 SLA
拒绝策略不是孤立配置项,它和你的 workQueue 类型强绑定。用 LinkedBlockingQueue(无界)基本不会触发拒绝策略——除非 OOM;而用 ArrayBlockingQueue(有界)才真正需要认真选策略。
- 如果你的业务要求“不能丢任何支付回调”,别犹豫,用
CallerRunsPolicy,但必须压测验证调用线程能否扛住峰值 - 如果你的系统要稳,又不想改太多代码,
AbortPolicy+ 全链路异常捕获 + 告警是最可控的起点 - 永远不要在生产环境用
DiscardPolicy或DiscardOldestPolicy而不加丢弃计数和采样日志 - 最容易被忽略的一点:拒绝策略只在
execute()触发,submit()返回Future时,异常可能藏在get()里,别漏掉










