当线程池任务提交超载且队列满、线程达最大值时,新任务被拒绝并由RejectedExecutionHandler处理;Java提供四种内置策略:AbortPolicy抛异常、CallerRunsPolicy由调用线程执行、DiscardPolicy静默丢弃、DiscardOldestPolicy丢弃队列头任务;推荐手动创建ThreadPoolExecutor指定策略,避免使用Executors;可自定义策略实现告警、重试等逻辑;应结合监控、有界队列、动态调参和任务分级预防拒绝。

在线程池中,当任务提交速度超过线程池处理能力,且队列已满、线程数已达最大值时,新任务就会被拒绝。此时 Java 会调用 RejectedExecutionHandler 来处理这些“溢出任务”。关键不是简单设置一个策略,而是理解每种策略的行为、适用场景,并能自定义逻辑应对真实业务需求。
四种内置拒绝策略的区别与选择
Java 提供了四个标准实现,它们行为差异明显,不能随便选:
-
AbortPolicy(默认):直接抛出
RejectedExecutionException。适合不允许丢失任务、需快速失败并由上层捕获重试或告警的场景。 - CallerRunsPolicy:由提交任务的线程(即主线程)自己执行该任务。能减缓提交速度,避免雪崩,但可能阻塞调用方,影响响应时间。
- DiscardPolicy:静默丢弃任务,不报错也不通知。适合允许丢失、实时性要求低(如日志上报、埋点)的非关键任务。
-
DiscardOldestPolicy:丢弃队列头部最老的任务,再尝试提交当前任务。适合希望“保新弃旧”的场景(如最新行情数据比旧数据更重要),但要注意队列必须是
BlockingQueue且支持poll()。
如何在创建线程池时指定拒绝策略
所有基于 ThreadPoolExecutor 构造的线程池都支持传入 RejectedExecutionHandler。常用方式如下:
使用 ThreadPoolExecutor 构造器(推荐,控制粒度最细):
立即学习“Java免费学习笔记(深入)”;
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, // corePoolSize
4, // maxPoolSize
60L, // keepAliveTime
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(10), // 有界队列,触发拒绝的关键
new ThreadPoolExecutor.CallerRunsPolicy() // 指定策略
);
</font>
<p>使用 <code>Executors</code> 工具类创建后,需通过反射或包装改造(不推荐),因为其返回的线程池通常封装为 <code>FinalizableDelegatedExecutorService</code>,无法直接替换 handler。所以生产环境建议绕过 <code>Executors</code>,手写 <code>ThreadPoolExecutor</code>。</p>
<H3>自定义拒绝策略处理关键业务逻辑</H3>
<p>内置策略不够用?比如需要记录被拒任务到数据库、发告警、降级存入消息队列重试等。只需实现接口:</p>
<font color="gray">
<pre class="brush:php;toolbar:false;">
public class AlertAndRetryPolicy implements RejectedExecutionHandler {
private final BlockingQueue<Runnable> retryQueue;
public AlertAndRetryPolicy(BlockingQueue<Runnable> retryQueue) {
this.retryQueue = retryQueue;
}
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
// 1. 记录日志或发送监控告警
System.err.println("Task rejected: " + r + ", pool size: " +
executor.getPoolSize() + ", queue size: " +
executor.getQueue().size());
// 2. 尝试放入重试队列(注意容量和阻塞行为)
if (!retryQueue.offer(r)) {
System.err.println("Retry queue is full, dropping task.");
}
}
}
使用时传入即可:new AlertAndRetryPolicy(new LinkedBlockingQueue(100))。注意重试队列本身也要有保护机制,避免二次溢出。
拒绝发生前的预防与监控建议
依赖拒绝策略兜底不如提前预警和优化:
- 监控线程池核心指标:活跃线程数、队列长度、拒绝任务总数(
getRejectedExecutionCount())、任务处理耗时。 - 设置合理队列容量:无界队列(如
LinkedBlockingQueue默认容量 Integer.MAX_VALUE)极易掩盖问题,导致 OOM;优先选有界队列 + 明确拒绝策略。 - 动态调整参数:结合监控数据,在运行时通过
setCorePoolSize()、setMaximumPoolSize()调整(需确保线程池未 shutdown)。 - 任务分级:将核心任务与非核心任务分离,用不同线程池+不同策略,避免相互影响。


















