1. 触发时机 & 顶层接口定义
触发逻辑
ThreadPoolExecutor执行任务提交(execute()方法)时,当满足以下全部条件会触发拒绝策略:
- 核心线程数已打满
- 阻塞队列已填充满
- 最大线程数已打满
顶层接口
所有拒绝策略都实现RejectedExecutionHandler接口,仅定义1个核心方法:
public interface RejectedExecutionHandler {
/**
* @param r 被拒绝的任务
* @param executor 当前触发拒绝的线程池实例
*/
void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}
JDK内置了4种实现,都是
ThreadPoolExecutor的静态内部类,默认使用AbortPolicy。
2. 内置拒绝策略源码分析
2.1 AbortPolicy(默认策略)
public static class AbortPolicy implements RejectedExecutionHandler {
public AbortPolicy() { }
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
// 直接抛出运行时异常,通知业务任务被拒
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " + e.toString());
}
}
✅ 特点:
- 是线程池默认的拒绝策略,显式抛出异常,业务可以捕获后做降级处理
- 适合核心链路、需要感知任务失败的场景
⚠️ 注意:如果未捕获异常会直接导致提交任务的线程崩溃
2.2 CallerRunsPolicy
public static class CallerRunsPolicy implements RejectedExecutionHandler {
public CallerRunsPolicy() { }
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
// 线程池未关闭的前提下,直接由提交任务的当前线程执行任务
r.run();
}
}
}
✅ 特点:
- 不会丢失任务,通过同步执行的方式降低任务提交速率,给线程池留出缓冲时间
- 适合流量尖峰不持续、不允许任务丢失的低并发场景
⚠️ 注意:高并发场景下会占用提交任务的线程(比如HTTP服务的NIO线程),导致服务对外响应能力下降
2.3 DiscardPolicy
public static class DiscardPolicy implements RejectedExecutionHandler {
public DiscardPolicy() { }
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
// 空实现,直接静默丢弃任务,无任何通知
}
}
✅ 特点:
- 无感知丢弃新提交的任务,没有额外性能开销
- 适合非核心、可丢失的任务场景:比如埋点上报、非关键日志打印
⚠️ 注意:绝对不能用于核心业务链路,任务丢失无法感知
2.4 DiscardOldestPolicy
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
public DiscardOldestPolicy() { }
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
// 先丢弃队列中等待最久的队头任务
e.getQueue().poll();
// 重新提交当前新任务
e.execute(r);
}
}
}
✅ 特点:
- 牺牲旧任务保证新任务执行,适合新任务优先级更高的场景:比如实时消息通知、最新状态同步
⚠️ 注意:如果阻塞队列是PriorityBlockingQueue(优先级队列),该策略会直接丢弃优先级最高的待执行任务,这种场景禁止使用
3. 自定义拒绝策略实现
如果内置策略不满足业务需求,可以直接实现RejectedExecutionHandler自定义逻辑,比如把被拒绝的任务持久化后定时重试:
public class PersistRejectPolicy implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
// 1. 序列化任务后存入数据库/Redis
TaskStorage.save(new TaskRecord(r, executor.getCorePoolSize()));
// 2. 打印告警日志
log.warn("任务被拒绝,已持久化待重试:{}", r);
}
}
4. 选型参考
| 策略 | 适用场景 | 注意事项 |
|---|---|---|
| AbortPolicy | 核心链路、需要感知任务失败的场景 | 必须捕获异常避免提交线程崩溃 |
| CallerRunsPolicy | 低并发、不允许丢任务的场景 | 高并发下会占用上游线程,拖慢服务响应 |
| DiscardPolicy | 非核心、可丢失的边缘任务 | 禁止用于核心链路 |
| DiscardOldestPolicy | 新任务优先级高于旧任务的场景 | 不能和优先级队列一起使用 |
评论区