DelayQueue是Java并发包中基于PriorityQueue实现的无界阻塞队列,元素必须实现Delayed接口,按到期时间升序排列,仅到期元素可被取出;不支持null及非Delayed对象。

DelayQueue 是 Java 并发包中一个无界阻塞队列,专为延时任务调度设计。它内部基于优先级队列(PriorityQueue)实现,元素必须实现 Delayed 接口,按到期时间升序排列,只有到期的元素才能被取出。它不支持 null 元素,也不允许插入非 Delayed 类型对象。
让任务具备延时能力:实现 Delayed 接口
核心是定义一个可延时的任务类,实现 Delayed 接口。该接口要求重写两个方法:
- getDelay(TimeUnit unit):返回当前剩余延迟时间(单位由参数指定),若 ≤ 0 表示已到期;
- compareTo(Delayed other):用于队列内部排序,通常按到期时间比较,确保最早到期的任务排在队首。
注意:getDelay 返回值应基于系统纳秒时间(推荐用 System.nanoTime()),避免因系统时钟回拨导致异常行为。例如:
public class DelayTask implements Delayed {
private final long triggerTime; // 纳秒时间戳
private final String jobName;
<pre class="brush:php;toolbar:false;">public DelayTask(long delayMs, String name) {
this.triggerTime = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(delayMs);
this.jobName = name;
}
@Override
public long getDelay(TimeUnit unit) {
long nanosLeft = triggerTime - System.nanoTime();
return unit.convert(nanosLeft, TimeUnit.NANOSECONDS);
}
@Override
public int compareTo(Delayed o) {
return Long.compare(this.triggerTime, ((DelayTask) o).triggerTime);
}
@Override
public String toString() {
return "DelayTask{" + "jobName='" + jobName + "', triggerTime=" + triggerTime + '}';
}}
立即学习“Java免费学习笔记(深入)”;
启动调度线程:从 DelayQueue 中安全取任务
DelayQueue 的 take() 方法会阻塞,直到队首元素到期;poll() 则立即返回(可能为 null)。实际调度中一般搭配一个守护线程循环调用 take(),拿到任务后交由线程池执行,避免阻塞影响调度精度:
- 不要在
take()后直接执行耗时逻辑,否则会卡住整个调度线程; - 建议使用独立的业务线程池处理任务,保持调度线程轻量;
- 需捕获并处理
InterruptedException,避免线程意外终止。
示例调度器片段:
DelayQueue<DelayTask> queue = new DelayQueue<>();
ExecutorService workerPool = Executors.newFixedThreadPool(4);
<p>Thread scheduler = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
try {
DelayTask task = queue.take(); // 阻塞等待到期
workerPool.submit(() -> System.out.println("执行:" + task.jobName));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
});
scheduler.setDaemon(true);
scheduler.start();动态添加与取消任务:注意不可删除特性
DelayQueue 不提供基于内容的删除方法(如 remove(Object) 效率极低,且不保证原子性)。因此:
- 任务一旦入队,无法主动取消(除非任务自身可中断,或依赖外部状态判断是否跳过执行);
- 若需支持取消,可在任务对象中加 volatile 标志位,在执行前检查是否已被标记为失效;
- 添加任务用
offer()即可,线程安全,无需额外同步。
例如增强版任务类可加:
private volatile boolean cancelled = false;
public void cancel() { this.cancelled = true; }
// 执行前检查:if (!task.cancelled) { ... }适用场景与局限提醒
DelayQueue 适合轻量、精度要求不高(毫秒级)、任务量中等(如几千以内)的延时调度,比如:
- 订单超时关闭
- 连接空闲检测
- 缓存预热触发
但它不适合:
- 高并发大量延时任务(底层 PriorityQueue 插入/删除为 O(log n),大量任务频繁增删影响性能);
- 需要持久化或集群调度的场景(进程重启后任务丢失);
- 对延迟精度要求微秒级或需严格顺序保障的场合。
更复杂需求建议选用 Quartz、XXL-JOB 或 Redis + Sorted Set 方案。



















