在 Java 中,线程池的拒绝策略用于处理当线程池已满(线程数达最大值且工作队列已满)时新提交的任务。Java 内置了 4 种拒绝策略,均实现了RejectedExecutionHandler接口,同时也支持自定义拒绝策略。以下是详细说明:
1. AbortPolicy(中止策略)
- 特点:默认拒绝策略,当任务被拒绝时,直接抛出
RejectedExecutionException异常,明确告知任务提交失败,阻止系统继续执行。 - 使用场景:适用于关键任务,需要明确知道任务是否执行成功,不允许任务丢失的场景(如金融交易、数据一致性要求高的操作)。
- 代码示例:
import java.util.concurrent.*;
public class AbortPolicyExample {
public static void main(String[] args) {
// 创建线程池:核心线程1,最大线程1,队列容量1
ThreadPoolExecutor executor = new ThreadPoolExecutor(
1, 1, 0L, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(1),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy() // 指定中止策略
);
// 提交3个任务(超过线程池处理能力)
for (int i = 0; i < 3; i++) {
final int taskNum = i;
try {
executor.submit(() -> {
System.out.println("执行任务:" + taskNum);
try { Thread.sleep(1000); } catch (InterruptedException e) {}
});
} catch (RejectedExecutionException e) {
System.out.println("任务" + taskNum + "被拒绝:" + e.getMessage());
}
}
executor.shutdown();
}
}
- 输出:前两个任务执行,第三个任务抛出异常被拒绝。
2. CallerRunsPolicy(调用者运行策略)
- 特点:任务被拒绝时,由提交任务的线程(调用者线程)亲自执行该任务,减缓新任务提交速度,给线程池留出处理时间,避免任务丢失。
- 使用场景:适用于任务提交频率不高,且不希望任务丢失的场景(如非核心但需要执行的后台任务,避免系统过载)。
- 代码示例:
import java.util.concurrent.*;
public class CallerRunsPolicyExample {
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
1, 1, 0L, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(1),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy() // 指定调用者运行策略
);
for (int i = 0; i < 3; i++) {
final int taskNum = i;
executor.submit(() -> {
System.out.println("执行任务" + taskNum + ",执行线程:" + Thread.currentThread().getName());
try { Thread.sleep(1000); } catch (InterruptedException e) {}
});
}
executor.shutdown();
}
}
- 输出:前两个任务由线程池线程执行,第三个任务由主线程(main)执行(调用者线程)。
3. DiscardPolicy(丢弃策略)
- 特点:任务被拒绝时,直接丢弃该任务,不做任何处理,也不抛出异常,任务无声消失。
- 使用场景:适用于非关键任务,任务丢失不影响系统核心功能(如日志收集、统计上报等可容忍少量数据丢失的场景)。
- 代码示例:
import java.util.concurrent.*;
public class DiscardPolicyExample {
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
1, 1, 0L, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(1),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardPolicy() // 指定丢弃策略
);
for (int i = 0; i < 3; i++) {
final int taskNum = i;
executor.submit(() -> {
System.out.println("执行任务:" + taskNum);
try { Thread.sleep(1000); } catch (InterruptedException e) {}
});
}
executor.shutdown();
}
}
- 输出:仅前两个任务执行,第三个任务被直接丢弃,无任何提示。
4. DiscardOldestPolicy(丢弃最旧任务策略)
- 特点:任务被拒绝时,丢弃工作队列中最旧的任务(队列头部的任务),然后尝试将新任务加入队列。
- 使用场景:适用于希望新任务优先执行,且旧任务可被替代的场景(如实时数据处理,新数据比旧数据更有价值)。
- 代码示例:
import java.util.concurrent.*;
public class DiscardOldestPolicyExample {
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
1, 1, 0L, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(1),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardOldestPolicy() // 指定丢弃最旧任务策略
);
for (int i = 0; i < 3; i++) {
final int taskNum = i;
executor.submit(() -> {
System.out.println("执行任务:" + taskNum);
try { Thread.sleep(1000); } catch (InterruptedException e) {}
});
}
executor.shutdown();
}
}
- 输出:任务 0 执行,任务 1 被放入队列,任务 2 提交时触发策略:丢弃队列中最旧的任务 1,将任务 2 加入队列,最终执行任务 0 和任务 2。
5. 自定义拒绝策略
- 特点:通过实现
RejectedExecutionHandler接口,自定义任务被拒绝时的处理逻辑(如记录日志、持久化任务到数据库、重试等)。 - 使用场景:适用于业务复杂的场景(如任务必须执行,需保存到本地文件或消息队列,后续重试)。
- 代码示例:
import java.util.concurrent.*;
// 自定义拒绝策略:记录日志并保存任务
class CustomRejectPolicy implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println("任务" + r.toString() + "被拒绝,已记录日志并保存到数据库");
// 实际场景中可添加:保存任务到数据库/消息队列,后续重试
}
}
public class CustomPolicyExample {
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
1, 1, 0L, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(1),
Executors.defaultThreadFactory(),
new CustomRejectPolicy() // 使用自定义策略
);
for (int i = 0; i < 3; i++) {
final int taskNum = i;
executor.submit(() -> {
System.out.println("执行任务:" + taskNum);
try { Thread.sleep(1000); } catch (InterruptedException e) {}
});
}
executor.shutdown();
}
}
- 输出:前两个任务执行,第三个任务被自定义策略处理(打印日志)。
总结
选择拒绝策略时需根据业务对任务的重要性、容忍丢失程度、系统负载等因素决定。内置策略适用于简单场景,复杂场景可通过自定义策略灵活处理。
170万+

被折叠的 条评论
为什么被折叠?



