常见的线程池饱和策略及使用场景
当线程池的任务队列已满且线程数达到最大值时,新的任务会触发饱和策略(拒绝策略)。Java 提供了四种默认策略:
-
AbortPolicy(默认策略)
行为 :直接抛出RejectedExecutionException
异常。
适用场景:- 需要严格保证任务不丢失的场景(如支付交易)。
- 调用方需明确感知任务提交失败,以便回滚或重试。
示例:金融系统中订单处理,失败需立即告警。
-
CallerRunsPolicy
行为 :由提交任务的线程(调用者线程)直接执行该任务。
适用场景:- 需要避免任务丢失,且能接受任务执行速度下降。
- 利用调用线程执行任务,变相限制新任务提交速率(负反馈)。
示例:Web 服务器处理请求,高峰期用用户线程执行任务,防止雪崩。
-
DiscardPolicy
行为 :直接丢弃新任务,无任何通知。
适用场景:- 允许静默丢弃非关键任务(如日志收集)。
- 对实时性要求低且可容忍数据丢失的场景。
示例:监控数据上报,丢弃部分数据不影响整体统计。
-
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();
}
}
自定义策略的常见实践
-
记录日志/告警:
javapublic void rejectedExecution(Runnable r, ThreadPoolExecutor e) { log.error("任务拒绝: {}", r, new RejectedException()); }
-
暂存到数据库/Redis:
javapublic void rejectedExecution(Runnable r, ThreadPoolExecutor e) { taskRedisQueue.save(r); // 保存到外部存储,后续恢复 }
-
有限次重试:
javaprivate int maxRetries = 3; public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (retryCount.getAndIncrement() < maxRetries) { e.execute(r); // 重试提交 } }
-
降级处理:
javapublic void rejectedExecution(Runnable r, ThreadPoolExecutor e) { runFallbackService((Task) r); // 执行降级逻辑 }
总结
策略 | 行为 | 适用场景 |
---|---|---|
AbortPolicy |
抛出异常 | 需严格保证任务不丢失(如交易系统) |
CallerRunsPolicy |
调用者线程执行任务 | 限制提交速率,避免雪崩(如Web服务) |
DiscardPolicy |
静默丢弃新任务 | 容忍数据丢失(如日志收集) |
DiscardOldestPolicy |
丢弃队首任务并重试提交新任务 | 新任务优先级更高(如实时推送) |
自定义策略 :通过实现 RejectedExecutionHandler
接口,可结合业务需求实现降级、重试、持久化等灵活逻辑。