侧边栏壁纸
博主头像
xh博主等级

永远是一个学者

  • 累计撰写 15 篇文章
  • 累计创建 6 个标签
  • 累计收到 8 条评论
标签搜索

目 录CONTENT

文章目录

Java线程池拒绝策略源码解析

xh
xh
2023-07-21 / 0 评论 / 0 点赞 / 66 阅读 / 962 字
温馨提示:
本文最后更新于 2026-03-18,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

1. 触发时机 & 顶层接口定义

触发逻辑

ThreadPoolExecutor执行任务提交(execute()方法)时,当满足以下全部条件会触发拒绝策略:

  1. 核心线程数已打满
  2. 阻塞队列已填充满
  3. 最大线程数已打满
顶层接口

所有拒绝策略都实现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 新任务优先级高于旧任务的场景 不能和优先级队列一起使用
0

评论区