【线程池饱和策略】线程池饱和策略及自定义方法

常见的线程池饱和策略及使用场景

当线程池的任务队列已满且线程数达到最大值时,新的任务会触发饱和策略(拒绝策略)。Java 提供了四种默认策略:

  1. AbortPolicy(默认策略)
    行为 :直接抛出 RejectedExecutionException 异常。
    适用场景:

    • 需要严格保证任务不丢失的场景(如支付交易)。
    • 调用方需明确感知任务提交失败,以便回滚或重试。
      示例:金融系统中订单处理,失败需立即告警。
  2. CallerRunsPolicy
    行为 :由提交任务的线程(调用者线程)直接执行该任务。
    适用场景:

    • 需要避免任务丢失,且能接受任务执行速度下降。
    • 利用调用线程执行任务,变相限制新任务提交速率(负反馈)。
      示例:Web 服务器处理请求,高峰期用用户线程执行任务,防止雪崩。
  3. DiscardPolicy
    行为 :直接丢弃新任务,无任何通知。
    适用场景:

    • 允许静默丢弃非关键任务(如日志收集)。
    • 对实时性要求低且可容忍数据丢失的场景。
      示例:监控数据上报,丢弃部分数据不影响整体统计。
  4. DiscardOldestPolicy
    行为 :丢弃队列中最旧的任务(队首),然后重试提交新任务。
    适用场景:

    • 新任务优先级高于旧任务(如实时消息推送)。
    • 可容忍历史任务延迟或丢失。
      注意 :优先队列中"最旧"不一定是提交时间最早,而是优先级最低。
      示例:股票价格更新,最新价格比历史价格更重要。

如何自定义饱和策略?

通过实现 RejectedExecutionHandler 接口并重写 rejectedExecution 方法:

java 复制代码
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;

public class CustomRejectionPolicy implements RejectedExecutionHandler {
    @Override
    public void rejectedExecution(Runnable task, ThreadPoolExecutor executor) {
        // 自定义处理逻辑(示例:记录日志 + 重试一次)
        System.err.println("任务被拒绝: " + task);
        
        if (!executor.isShutdown()) {
            // 重试一次
            executor.execute(task);
            System.out.println("任务重试提交成功");
        }
    }
}

使用自定义策略

java 复制代码
import java.util.concurrent.*;

public class ThreadPoolDemo {
    public static void main(String[] args) {
        // 创建线程池并指定自定义策略
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            2, // 核心线程数
            4, // 最大线程数
            60, TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(2), // 容量为2的队列
            new CustomRejectionPolicy()   // 自定义饱和策略
        );

        // 提交任务(模拟饱和)
        for (int i = 0; i < 10; i++) {
            executor.execute(() -> {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }
        executor.shutdown();
    }
}

自定义策略的常见实践

  1. 记录日志/告警:

    java 复制代码
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        log.error("任务拒绝: {}", r, new RejectedException());
    }
  2. 暂存到数据库/Redis:

    java 复制代码
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        taskRedisQueue.save(r); // 保存到外部存储,后续恢复
    }
  3. 有限次重试:

    java 复制代码
    private int maxRetries = 3;
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (retryCount.getAndIncrement() < maxRetries) {
            e.execute(r); // 重试提交
        }
    }
  4. 降级处理:

    java 复制代码
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        runFallbackService((Task) r); // 执行降级逻辑
    }

总结

策略 行为 适用场景
AbortPolicy 抛出异常 需严格保证任务不丢失(如交易系统)
CallerRunsPolicy 调用者线程执行任务 限制提交速率,避免雪崩(如Web服务)
DiscardPolicy 静默丢弃新任务 容忍数据丢失(如日志收集)
DiscardOldestPolicy 丢弃队首任务并重试提交新任务 新任务优先级更高(如实时推送)

自定义策略 :通过实现 RejectedExecutionHandler 接口,可结合业务需求实现降级、重试、持久化等灵活逻辑。

相关推荐
写后端的胖头鱼3 天前
如何区分IO/CPU 密集型,线程池核心参数怎么设置
java·线程池·多线程·io密集·cpu密集
随遇而安zx15 天前
【多线程】---线程池 ThreadPoolExecutor 原理与调优
java·线程池·多线程
ly768915 天前
Spring 异步编程的隐藏风险:@Async 线程池耗尽与异常处理详解
java·后端·spring·异常处理·线程池·任务拒绝
~木雨17 天前
Java 并发编程架构全景:并发层的五域设计 —— 线程模型、线程池隔离、并发安全到问题治理(全体系汇总)
java·安全·架构·线程池·并发编程·高并发架构
琥珀色糖17 天前
Linux手搓线程池!(C和C++版)
线程池
~木雨19 天前
Java 线程池七问七答:参数、执行流程、拒绝策略到 ThreadLocal 内存泄漏,面试必背
java·面试·线程池·threadlocal·threadpool·executor
小宇宙清歡渡23 天前
SpringBoot JDK17 线程池的使用
springboot·线程池·jdk17
AI人工智能+电脑小能手1 个月前
大白话说Java设计模式-37-中介者模式(源码剖析篇)
java·设计模式·线程池·中介者模式·源码分析·executor·applicationeventmulticaster
JacksonMx1 个月前
CompletableFuture 生产环境踩坑详解与解决方案
spring boot·spring·线程池
lisin-lee-cooper1 个月前
简单聊聊线程池源码
java·线程池