在Java并发编程场景中,ThreadPoolExecutor是官方提供的核心线程池实现类,当我们向线程池提交任务时,如果核心线程都在工作、工作队列已满且线程数达到最大线程数,此时新提交的任务就会触发线程池的饱和策略。饱和策略的作用是定义线程池无法继续处理新任务时的应对规则,避免任务丢失或者系统崩溃。

ThreadPoolExecutor的饱和策略类型
ThreadPoolExecutor内置了四种默认的饱和策略,都实现了RejectedExecutionHandler接口,我们可以通过ThreadPoolExecutor的构造方法或者setRejectedExecutionHandler方法来设置对应的策略。
1. AbortPolicy(默认策略)
这是ThreadPoolExecutor的默认饱和策略,当触发饱和时,会直接抛出RejectedExecutionException异常,阻止系统继续提交任务,适合需要明确感知任务提交失败的场景。
import java.util.concurrent.*;
public class AbortPolicyDemo {
public static void main(String[] args) {
// 创建线程池:核心线程1,最大线程1,队列容量1,使用默认AbortPolicy
ThreadPoolExecutor executor = new ThreadPoolExecutor(
1,
1,
0L,
TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(1)
);
// 提交3个任务,第3个会触发饱和策略
for (int i = 0; i < 3; i++) {
int taskId = i;
try {
executor.execute(() -> {
System.out.println("执行任务:" + taskId);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
} catch (RejectedExecutionException e) {
System.out.println("任务" + taskId + "被拒绝,触发AbortPolicy");
}
}
executor.shutdown();
}
}
2. CallerRunsPolicy
该策略会将被拒绝的任务交给提交任务的线程来执行,也就是调用execute方法的线程会自己运行这个任务。这种策略可以降低新任务的提交速度,给线程池缓冲时间,适合对任务提交速率有自我调节需求的场景。
import java.util.concurrent.*;
public class CallerRunsPolicyDemo {
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
1,
1,
0L,
TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(1),
new ThreadPoolExecutor.CallerRunsPolicy()
);
for (int i = 0; i < 3; i++) {
int taskId = i;
executor.execute(() -> {
System.out.println("执行任务:" + taskId + ",执行线程:" + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
executor.shutdown();
}
}
3. DiscardPolicy
当触发饱和时,该策略会直接丢弃被拒绝的任务,不会抛出任何异常,也不会执行任务。适合那些允许任务丢失、对任务完整性要求不高的场景,比如一些非核心的日志上报任务。
import java.util.concurrent.*;
public class DiscardPolicyDemo {
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
1,
1,
0L,
TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(1),
new ThreadPoolExecutor.DiscardPolicy()
);
for (int i = 0; i < 3; i++) {
int taskId = i;
executor.execute(() -> {
System.out.println("执行任务:" + taskId);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
executor.shutdown();
}
}
4. DiscardOldestPolicy
该策略会丢弃队列中等待时间最长的任务,也就是队列头部的任务,然后尝试将新提交的任务加入队列。如果此时队列还是满的,就会继续丢弃,直到能加入为止。适合需要优先处理新任务的场景。
import java.util.concurrent.*;
public class DiscardOldestPolicyDemo {
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
1,
1,
0L,
TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(1),
new ThreadPoolExecutor.DiscardOldestPolicy()
);
for (int i = 0; i < 3; i++) {
int taskId = i;
executor.execute(() -> {
System.out.println("执行任务:" + taskId);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
executor.shutdown();
}
}
自定义饱和策略
如果内置的四种饱和策略无法满足业务需求,我们可以自己实现RejectedExecutionHandler接口,自定义饱和策略的逻辑。比如我们需要将被拒绝的任务记录到日志中,同时保存到本地文件后续重试,就可以这样实现:
import java.util.concurrent.*;
import java.io.FileWriter;
import java.io.IOException;
// 自定义饱和策略:记录被拒绝的任务到日志,并保存到文件
class CustomRejectPolicy implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
// 记录任务信息
System.out.println("任务被拒绝,任务信息:" + r.toString());
// 保存到本地文件
try (FileWriter writer = new FileWriter("reject_task.log", true)) {
writer.write("被拒绝的任务:" + r.toString() + "n");
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class CustomPolicyDemo {
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
1,
1,
0L,
TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(1),
new CustomRejectPolicy()
);
for (int i = 0; i < 3; i++) {
int taskId = i;
executor.execute(() -> {
System.out.println("执行任务:" + taskId);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
executor.shutdown();
}
}
饱和策略的选择建议
不同的业务场景需要选择不同的饱和策略,我们可以参考以下建议:
- 如果任务非常重要,不允许丢失,也不允许系统无感知丢弃,选择
AbortPolicy,通过捕获异常来处理失败任务 - 如果希望提交任务的线程能感知到线程池过载,并且主动降低提交速度,选择
CallerRunsPolicy - 如果任务属于非核心业务,允许丢失,选择
DiscardPolicy - 如果新提交的任务比队列中等待的旧任务更重要,选择
DiscardOldestPolicy - 如果有特殊的任务处理需求,比如持久化、告警等,选择自定义饱和策略
注意事项
在使用饱和策略时,需要注意以下几点:
- 饱和策略只在核心线程满、队列满、最大线程也满的情况下才会触发,所以合理设置核心线程数、最大线程数和队列容量是避免频繁触发饱和策略的前提
- 如果使用了
CallerRunsPolicy,提交任务的线程会被阻塞直到任务执行完成,可能会影响该线程的其他业务逻辑,需要评估影响范围 - 自定义饱和策略中不要做太耗时的操作,避免影响线程池的正常调度
ThreadPoolExecutor饱和策略Java线程池RejectedExecutionHandler修改时间:2026-07-12 11:12:30