当线程池的任务队列满了并且线程池中的工作线程数已经达到 maximumPoolSize
时,线程池将无法接受新的任务。在这种情况下,ThreadPoolExecutor
会使用您指定的拒绝策略 (RejectedExecutionHandler
) 处理新提交的任务。
主要的拒绝策略
Java 提供了四种内置的拒绝策略,定义在 java.util.concurrent.ThreadPoolExecutor
类中:
-
AbortPolicy(默认)
- 行为:抛出
RejectedExecutionException
异常。 - 用法:不希望任务丢失,并能捕获和处理异常的场景。
RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();
- 行为:抛出
-
CallerRunsPolicy
- 行为:由调用线程执行任务。
- 用法:希望任务在某种程度上仍然被执行,同时减少对线程池的压力的场景。
RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy();
-
DiscardPolicy
- 行为:直接丢弃任务,不抛异常。
- 用法:可以容忍丢失任务的场景。
RejectedExecutionHandler handler = new ThreadPoolExecutor.DiscardPolicy();
-
DiscardOldestPolicy
- 行为:丢弃队列中最早的排队任务,然后尝试重新提交新任务。
- 用法:希望丢弃旧任务,优先执行新任务的场景。
RejectedExecutionHandler handler = new ThreadPoolExecutor.DiscardOldestPolicy();
示例
下面是一个示例,展示当任务队列满了且线程数达到 maximumPoolSize
时,不同拒绝策略的行为:
import java.util.concurrent.*;
public class RejectedExecutionHandlerExample {
public static void main(String[] args) {
// 核心参数
int corePoolSize = 2;
int maximumPoolSize = 4;
long keepAliveTime = 10;
TimeUnit unit = TimeUnit.SECONDS;
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(2);
// 拒绝策略,可以替换为其他策略以查看不同效果
RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();
// RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy();
// RejectedExecutionHandler handler = new ThreadPoolExecutor.DiscardPolicy();
// RejectedExecutionHandler handler = new ThreadPoolExecutor.DiscardOldestPolicy();
// 创建线程池
ExecutorService threadPool = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
unit,
workQueue,
Executors.defaultThreadFactory(),
handler);
for (int i = 0; i < 10; i++) {
final int taskNumber = i;
try {
threadPool.submit(() -> {
try {
System.out.println("Task " + taskNumber + " is running by " + Thread.currentThread().getName());
Thread.sleep(2000); // 模拟任务执行时间
System.out.println("Task " + taskNumber + " is completed by " + Thread.currentThread().getName());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
} catch (RejectedExecutionException e) {
System.out.println("Task " + taskNumber + " was rejected");
}
}
// 关闭线程池
threadPool.shutdown();
try {
if (!threadPool.awaitTermination(60, TimeUnit.SECONDS)) {
threadPool.shutdownNow();
}
} catch (InterruptedException ex) {
threadPool.shutdownNow();
Thread.currentThread().interrupt();
}
}
}
运行结果分析
在这里,您可以切换不同的 RejectedExecutionHandler
实现,并观察在控制台输出的不同反应:
-
AbortPolicy
- 抛出
RejectedExecutionException
异常并打印 “Task X was rejected” 错误信息。
- 抛出
-
CallerRunsPolicy
- 新任务将由调用线程执行,打印出来的任务可能由
main
线程处理。
- 新任务将由调用线程执行,打印出来的任务可能由
-
DiscardPolicy
- 默默地丢弃任务,没有任务排队的信息输出。
-
DiscardOldestPolicy
- 丢弃任务队列中最早的任务,新的任务插入队列并执行,由新的工作线程处理。
总结
当任务队列满了且工作线程数达到了 maximumPoolSize
时,线程池会根据指定的拒绝策略处理无法执行的新任务。理解和正确选择拒绝策略对保证系统的稳定性和合理处理突发高负载非常重要。不同的策略适用于不同的业务场景,开发者可以根据具体需求来选择合适的拒绝策略。