discardoldestpolicy 是 java threadpoolexecutor 提供的一种拒绝策略,其核心行为是丢弃线程池队列中等待时间最久的任务(即队头任务),然后尝试将当前新提交的任务重新加入队列。如果队列因此腾出了空间且线程池未关闭,新任务会被接受;否则将继续触发拒绝逻辑。

如何配置并使用 DiscardOldestPolicy
要使用此策略,需在创建 ThreadPoolExecutor 实例时将其作为第四个参数传入构造函数。该策略通常用于对实时性要求较高、允许丢失部分旧数据但必须保证最新数据被处理的场景,例如高频数据采集或控制指令下发。

import java.util.concurrent.*;
public class Main {
public static void main(String[] args) {
// 核心线程数2,最大线程数2,队列容量1
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, 2, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(1),
new ThreadPoolExecutor.DiscardOldestPolicy()
);
// 模拟提交任务
for (int i = 0; i < 5; i++) {
final int taskId = i;
try {
executor.submit(() -> {
System.out.println("Task " + taskId + " running on " + Thread.currentThread().getName());
try { Thread.sleep(2000); } catch (InterruptedException e) {}
});
} catch (RejectedExecutionException e) {
System.out.println("Task " + taskId + " rejected.");
}
}
executor.shutdown();
}
}
在上述代码中,当第3个任务提交时,由于队列已满且无空闲线程,会触发拒绝策略。若此时队头有等待任务,它将被丢弃,第3个任务尝试入队。若入队成功则执行,失败则再次抛出异常。实际运行中,前两个任务立即执行,第三个任务可能因队头被挤掉而进入队列或执行,后续任务视具体情况而定。
立即学习“Java免费学习笔记(深入)”;
DiscardOldestPolicy 与 CallerRunsPolicy 的行为差异
对比 CallerRunsPolicy,DiscardOldestPolicy 不会阻塞调用线程,而是直接修改内部状态(丢弃旧任务)。这意味着调用者不会感知到延迟,但可能丢失数据。而 CallerRunsPolicy 会在拒绝时由调用线程直接执行任务,起到背压作用。

import java.util.concurrent.*;
public class ComparePolicies {
public static void main(String[] args) throws InterruptedException {
// 测试 DiscardOldestPolicy
ThreadPoolExecutor exec1 = new ThreadPoolExecutor(
1, 1, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(1),
new ThreadPoolExecutor.DiscardOldestPolicy()
);
// 提交一个长时间运行的任务占住核心线程
exec1.submit(() -> {
try { Thread.sleep(5000); } catch (InterruptedException e) {}
});
Thread.sleep(100);
// 提交第二个任务,进入队列
exec1.submit(() -> System.out.println("Task 2 in Queue"));
Thread.sleep(100);
// 提交第三个任务,触发 DiscardOldestPolicy
// 它会丢弃 Task 2,并尝试提交自己
exec1.submit(() -> System.out.println("Task 3 Replaced Task 2"));
exec1.shutdown();
}
}
在上面的对比示例中,DiscardOldestPolicy 导致队列中的 Task 2 被移除,Task 3 获得执行机会。而在 CallerRunsPolicy 下,Task 3 将由主线程(caller)同步执行,直到完成才会返回,这会导致主线程阻塞。
当队列无法容纳新任务时会发生什么
即使使用了 DiscardOldestPolicy,如果线程池已达到最大线程数且队列已满,在丢弃队头任务后,新任务仍可能无法入队。此时,execute 方法会再次检查队列状态,若依然无法添加,则继续抛出 RejectedExecutionException。也就是说,DiscardOldestPolicy 并不保证新任务一定被接受,它只是提供了一次“重试”入队的机会。


















