线程池拒绝策略
Abstract
当线程池和工作队列都满时,新提交的任务会被拒绝,触发
RejectedExecutionHandler。
四种内置策略
| 策略 | 行为 | 适用场景 |
|---|---|---|
AbortPolicy | 抛出 RejectedExecutionException(默认) | 需要快速失败的场景 |
CallerRunsPolicy | 由提交任务的线程执行该任务 | 非核心任务,允许降级 |
DiscardPolicy | 静默丢弃,不抛异常 | 不重要的任务(如日志) |
DiscardOldestPolicy | 丢弃队列中最旧的任务,然后重新提交 | 实时性要求高的任务 |
策略详解
AbortPolicy(默认)
// 默认策略:线程池满时直接抛出异常
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, 4, 60L, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(100)
// 默认就是 AbortPolicy,不指定也行
);适用场景:任务不可丢失,需要调用方感知并处理异常。
CallerRunsPolicy(推荐)
// 推荐策略:由调用者线程执行,天然降级
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, 4, 60L, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(100),
new ThreadPoolExecutor.CallerRunsPolicy()
);优点:
- 不会丢失任务
- 调用者线程执行任务时无法提交新任务,形成负反馈(天然限流)
- 适合批量数据处理场景
风险:调用者线程可能被长时间阻塞。
DiscardPolicy
// 静默丢弃
new ThreadPoolExecutor.DiscardPolicy()适用场景:非关键任务(如统计日志、埋点上报),丢了不影响核心业务。
使用 DiscardPolicy 时务必记录日志或监控
静默丢弃意味着你永远不会知道任务被丢了,建议配套日志告警。
DiscardOldestPolicy
// 丢弃最旧任务
new ThreadPoolExecutor.DiscardOldestPolicy()适用场景:实时性要求高,新任务比旧任务更有价值的场景(如实时价格推送)。
DiscardOldestPolicy 与 PriorityBlockingQueue 的冲突
该策略会丢弃队列头部的任务。当使用
PriorityBlockingQueue时,头部是最优优先级的任务,丢弃它可能导致真正重要的任务被丢掉。
选型建议
| 场景 | 推荐策略 |
|---|---|
| 核心业务,任务不能丢 | CallerRunsPolicy(或自定义策略 + 告警) |
| 非核心,允许快速失败 | AbortPolicy |
| 非关键数据(日志/埋点) | DiscardPolicy(+ 监控告警) |
| 实时数据流(新>旧) | DiscardOldestPolicy |
自定义拒绝策略
// 自定义策略:记录日志后写死信队列
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, 4, 60L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(100),
(r, executor1) -> {
// 记录告警
log.warn("任务被拒绝,线程池已满,队列已满");
// 写入死信队列或数据库
deadLetterQueue.offer(r);
}
);相关笔记
- ThreadPoolExecutor核心参数 — 线程池构造参数与工作流程
- Java线程池创建方式 — 线程池创建的实际配置
- JVM内存溢出预防 — 拒绝策略是防 OOM 的最后一道防线