线程池的四种拒绝策略各适用什么场景?

结论先行:当任务队列已满且线程数达到 maximumPoolSize 时,新任务会交给拒绝策略处理。ThreadPoolExecutor 内置四种策略:AbortPolicy(抛异常)、CallerRunsPolicy(调用者线程执行)、DiscardPolicy(静默丢弃)、DiscardOldestPolicy(丢弃队列中最老任务),也可以实现 RejectedExecutionHandler 自定义降级逻辑。

四种策略对比

策略行为风险 / 适用场景
AbortPolicy(默认)抛 RejectedExecutionException快速暴露过载,适合可接受失败并重试的业务
CallerRunsPolicy由提交任务的线程直接执行天然背压限流、不丢任务,适合写库等关键任务
DiscardPolicy悄悄丢弃新任务适合可容忍丢失的日志、统计类任务
DiscardOldestPolicy丢弃队头最老任务再重试提交追求“留新弃旧”,如实时价格/行情刷新

使用注意

  • 任何拒绝都说明“池 + 队列”容量配小了,应先按 QPS × 平均响应时间估算后再定参。
  • 生产环境常用自定义策略:记录告警日志并降级(如转存 MQ、走兜底数据源),而不是裸抛异常。
import java.util.concurrent.*;

public class RejectDemo {
    public static void main(String[] args) {
        ThreadPoolExecutor pool = new ThreadPoolExecutor(
                1, 1, 0L, TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<>(1),
                new ThreadPoolExecutor.CallerRunsPolicy()); // 慢下来而不是丢

        for (int i = 0; i < 5; i++) {
            final int no = i;
            pool.execute(() -> System.out.println("执行 " + no));
        }
        pool.shutdown();
    }
}

常见追问 / 记忆点

  • 记忆点:Abort 抛异常、Caller 自己扛、Discard 静默丢、Oldest 丢最老。
  • 追问:默认策略是 AbortPolicy,最安全但也最“粗暴”,线上一般要自定义成降级+告警。
  • 追问:CallerRunsPolicy 把压力回传给提交线程,能形成“提交变慢 → 源头限流”的闭环。
  • 追问:真正的容量 = 队列长度 + maximumPoolSize;按 QPS×RT 估算后建议预留 20%~30% 余量。
笔记加载中…