Java 多线程编程之 RejectedExecutionHandler 线程池拒绝策略
RejectedExecutionHandler
public interface RejectedExecutionHandler {
void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}
- RejectedExecutionHandler 是 Java 线程池中用于处理任务被拒绝执行的拒绝处理器接口,当以下情况发生时,会调用拒绝处理器
-
线程池已关闭,即线程池调用了 shutdown 方法
-
线程池和任务队列都已满(达到最大线程数且队列无空间)
- Java 提供了 4 种内置的拒绝策略,它们都实现了 RejectedExecutionHandler 接口
一、AbortPolicy
1、基本介绍
public static class AbortPolicy implements RejectedExecutionHandler {
public AbortPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
-
默认策略,直接抛出 RejectedExecutionException 异常
-
生产环境最常用的策略,强制开发者处理拒绝情况
2、演示
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2,
2,
0,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(2),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
for (int i = 1; i <= 5; i++) {
int id = i;
try {
executor.execute(() -> {
System.out.println("执行任务 " + id + " 由 " + Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println("任务 " + id + " 提交成功");
} catch (RejectedExecutionException e) {
System.out.println("任务 " + id + " 被拒绝:" + e.getMessage());
}
}
- 输出结果
任务 1 提交成功
任务 2 提交成功
任务 3 提交成功
任务 4 提交成功
执行任务 1 由 pool-1-thread-1
任务 5 被拒绝:Task com.juc.RejectedExecutionHandlerTest$$Lambda$15/0x0000000800c01200@723279cf rejected from java.util.concurrent.ThreadPoolExecutor@10f87f48[Running, pool size = 2, active threads = 2, queued tasks = 2, completed tasks = 0]
执行任务 2 由 pool-1-thread-2
执行任务 3 由 pool-1-thread-1
执行任务 4 由 pool-1-thread-2
- 输出结果解读
1. 前 4 个任务提交成功(2 个在核心线程执行,2 个在队列中)
2. 第 5 个任务被拒绝,抛出 RejectedExecutionException 异常
二、CallerRunsPolicy
1、基本介绍
public static class CallerRunsPolicy implements RejectedExecutionHandler {
public CallerRunsPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
-
由提交任务的线程直接执行该任务,实现了一种简单的降级策略
-
适用于需要保证每个任务都能执行,且可以接受调用线程被用于执行任务的场景
2、演示
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2,
2,
0,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(2),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy());
for (int i = 1; i <= 5; i++) {
int id = i;
executor.execute(() -> {
System.out.println("执行任务 " + id + " 由 " + Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println("任务 " + id + " 提交成功");
}
- 输出结果
任务 1 提交成功
任务 2 提交成功
任务 3 提交成功
任务 4 提交成功
执行任务 2 由 pool-1-thread-2
执行任务 5 由 main
执行任务 1 由 pool-1-thread-1
任务 5 提交成功
执行任务 4 由 pool-1-thread-1
执行任务 3 由 pool-1-thread-2
- 输出结果解读
1. 前 4 个任务正常执行
2. 第 5 个任务由主线程执行
三、DiscardPolicy
1、基本介绍
public static class DiscardPolicy implements RejectedExecutionHandler {
public DiscardPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
-
不抛出异常,也不执行任务,丢弃被拒绝的任务
-
适用于可以容忍任务丢失的场景
2、演示
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2,
2,
0,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(2),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardPolicy());
for (int i = 1; i <= 5; i++) {
int id = i;
executor.execute(() -> {
System.out.println("执行任务 " + id + " 由 " + Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println("任务 " + id + " 提交成功");
}
- 输出结果
任务 1 提交成功
任务 2 提交成功
任务 3 提交成功
任务 4 提交成功
任务 5 提交成功
执行任务 2 由 pool-1-thread-2
执行任务 1 由 pool-1-thread-1
执行任务 3 由 pool-1-thread-1
执行任务 4 由 pool-1-thread-2
- 输出结果解读
1. 前 4 个任务正常执行
2. 第 5 个任务被丢弃,没有提示或异常
四、DiscardOldestPolicy
1、基本介绍
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
public DiscardOldestPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}
-
丢弃队列中最旧的任务(队头的任务)
-
适用于新任务比旧任务更重要,且可以接受丢失部分任务的场景
2、演示
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2,
2,
0,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(2),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardOldestPolicy());
for (int i = 1; i <= 5; i++) {
int id = i;
executor.execute(() -> {
System.out.println("执行任务 " + id + " 由 " + Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println("任务 " + id + " 提交成功");
}
- 输出结果
任务 1 提交成功
任务 2 提交成功
任务 3 提交成功
任务 4 提交成功
任务 5 提交成功
执行任务 1 由 pool-1-thread-1
执行任务 2 由 pool-1-thread-2
执行任务 4 由 pool-1-thread-2
执行任务 5 由 pool-1-thread-1
- 输出结果解读
1. 前 2 个任务由线程池线程执行,任务 3 和 4 进入队列
2. 当提交任务 5 时,队列中最老的任务 3 被丢弃,任务 5 进入队列
3. 最终执行的任务是 1、2、4、5