线程池拒绝策略是系统高负载下主动保护自身的机制,包括abortpolicy(抛异常)、callerrunspolicy(调用者执行)、discardpolicy(静默丢弃)、discardoldestpolicy(丢最老任务)及自定义策略(如日志、重试、告警)。

线程池拒绝策略不是“出错了才用”,而是系统在高负载下主动保护自身的关键机制。当线程数已达 maximumPoolSize 且任务队列已满时,新提交的任务必须被处理——此时拒绝策略决定是抛异常、丢任务、让调用者执行,还是换一种方式兜底。
AbortPolicy:默认但需谨慎使用
这是 ThreadPoolExecutor 的默认策略,行为简单粗暴:直接抛 RejectedExecutionException。它不隐藏问题,也不做任何妥协。
- 适合上游能快速感知失败并重试的场景,比如 RPC 异步回调、短生命周期的查询请求
- 配合监控告警效果更好,一旦触发说明系统已过载,需立刻干预
- 不适合后台聚合、日志写入等“丢了就丢”的任务,否则可能造成数据断点或业务逻辑断裂
CallerRunsPolicy:降速保命,缓解压力
任务被拒时,由提交任务的线程(通常是业务线程)自己执行该任务。这会自然拖慢任务提交节奏,给线程池喘息时间。
- 适用于不能丢任务、又不希望阻塞或抛异常的核心后台作业,如指标统计、异步审计日志
- 对吞吐敏感但对延迟容忍度较高的场景很有效,相当于把压力“反向传导”回上游
- 注意不要在高频调用路径(如 HTTP 请求入口)直接使用,否则可能拖垮整个请求线程池
DiscardPolicy 与 DiscardOldestPolicy:静默型策略要设限
这两个策略都不抛异常,容易掩盖风险,必须明确知道“丢得安心”才能用。
- DiscardPolicy:彻底静默丢弃,无日志、无提示。仅建议用于采样类任务,比如每千次请求记录一次 trace ID 做抽样分析
- DiscardOldestPolicy:丢掉队列中最老的任务,再尝试重新提交当前任务。看似“腾位置”,但可能反复挤掉同一任务,破坏执行公平性;只在极少数缓冲队列有强时效性(如实时行情快照)且可接受局部丢失时考虑
自定义拒绝策略:按需兜底,增强可观测性
内置策略覆盖不了所有现实需求。自定义策略可以做到记录日志、落库重试、发消息队列、触发熔断、甚至调用降级接口。
- 典型做法是在
rejectedExecution方法中加入结构化日志,包含任务类型、提交时间、线程池状态等上下文 - 对关键任务,可将被拒任务序列化后写入 Kafka 或 Redis,后续由补偿服务拉取重试
- 如果系统已接入告警平台,这里也是触发钉钉/企微告警的合理位置,避免靠人工巡检发现过载










