discardoldestpolicy在队列满时丢弃队列头部最老任务,再尝试提交新任务;仅对有界队列有效,通过poll()移除头元素、offer()插入新任务实现,不保证原子性且不中断运行中任务。

DiscardOldestPolicy 是 Java 线程池中一种拒绝策略,当线程池无法接受新任务(比如队列已满、线程数已达最大)时,它不会直接丢弃当前提交的任务,而是尝试丢弃**工作队列中最老的那个任务**,然后重新尝试把当前任务加入队列。
它怎么“丢弃最老任务”?
关键在于:这个操作发生在 add() 失败之后、且队列非空时。具体流程如下:
- 线程池尝试把新任务加入阻塞队列(如 LinkedBlockingQueue 或 ArrayBlockingQueue)
- 如果队列已满(add 返回 false),就触发拒绝策略
- DiscardOldestPolicy 会调用队列的 poll() 方法 —— 这个方法移除并返回队列头部元素(即最先入队、等待最久的任务)
- 接着再调用一次 offer()(或 add(),取决于队列实现),把当前新任务插入队列尾部
注意:这个过程不是原子的,中间可能有其他线程插入任务;而且如果 poll() 后 offer() 又失败(比如队列刚好被其他线程填满),那当前任务仍会被丢弃,不会重试。
只对有界队列有效
DiscardOldestPolicy 在无界队列(如默认的 LinkedBlockingQueue,capacity = Integer.MAX_VALUE)下基本不会触发,因为队列几乎不会满。它真正起作用的场景是:
- 使用 ArrayBlockingQueue 等固定容量队列
- 核心线程数小、最大线程数小、队列容量也小,容易积压
- 希望优先保障最新请求,容忍旧请求被挤掉
实际例子:丢弃的是谁?
假设你用的是 ArrayBlockingQueue
- Task A(先 submit,排在队首)
- Task B(后 submit,排在队尾)
此时再 submit Task C,队列已满 → 触发 DiscardOldestPolicy → poll() 移除 Task A → offer(Task C) 成功 → 最终队列变成 [Task B, Task C]。
也就是说,**被丢弃的是“排队时间最长”的那个,不一定是“最早创建”的,但通常是“最早等待执行”的任务。**
注意事项
- 该策略不中断正在执行的任务,只影响等待队列中的任务
- 如果队列是空的(比如所有任务都在运行,没排队),poll() 返回 null,当前任务仍会被丢弃
- 它不保证“绝对公平”,多线程并发下 poll + offer 之间存在竞态窗口
- 和 DiscardPolicy 不同,它试图挽救当前任务,而不是直接扔掉
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











