DelayQueue 是基于最小堆的无界阻塞队列,仅存 Delayed 元素,按 getDelay() 升序排列;需配合消费者线程 take() 执行任务,不支持主动删除未到期任务。

DelayQueue 是 Java 并发包中一个无界阻塞队列,专门用于存放实现了 Delayed 接口的元素,只有当元素的延迟时间到期后才能被取出。它天然适合实现“带过期时间的延时任务”,比如定时通知、缓存清理、订单超时关闭等场景。
理解 DelayQueue 的核心机制
DelayQueue 内部基于优先队列(最小堆),按任务的 getDelay(TimeUnit) 返回值升序排列。它不接受 null 元素,也不支持直接遍历(iterator() 不保证顺序)。关键点:
- 队列中的元素必须实现
Delayed接口(通常也实现Runnable或自定义任务类) -
getDelay()返回剩余延迟时间(负数表示已到期),单位由参数指定 -
compareTo()必须与getDelay()逻辑一致,否则排序失效 - 调用
take()会阻塞直到首个元素到期;poll()立即返回(可能为 null)
定义可延迟执行的任务类
推荐封装一个通用的延时任务,包含执行时间、业务逻辑和唯一标识(便于去重或取消):
例如:
public class DelayTask implements Runnable, Delayed {
private final long executeTime; // 任务应执行的时间戳(毫秒)
private final String taskId;
private final Runnable action;
<pre class="brush:php;toolbar:false;">public DelayTask(long delayMs, String taskId, Runnable action) {
this.executeTime = System.currentTimeMillis() + delayMs;
this.taskId = taskId;
this.action = action;
}
@Override
public long getDelay(TimeUnit unit) {
long remaining = executeTime - System.currentTimeMillis();
return unit.convert(remaining, TimeUnit.MILLISECONDS);
}
@Override
public int compareTo(Delayed o) {
return Long.compare(this.executeTime, ((DelayTask) o).executeTime);
}
@Override
public void run() {
action.run();
}}
启动后台线程消费 DelayQueue
DelayQueue 本身不自动执行任务,需配合一个消费者线程持续 take() 并触发执行:
- 使用
Thread或ExecutorService启动单个守护线程即可(避免多个线程竞争) - 捕获运行时异常,防止任务失败导致线程退出
- 建议加日志记录执行时间与任务 ID,便于排查延迟偏差
示例消费者:
DelayQueue<delaytask> queue = new DelayQueue();
new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
try {
DelayTask task = queue.take(); // 阻塞等待到期
task.run();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
} catch (Throwable t) {
// 记录异常但不停止线程
System.err.println("Task execution failed: " + t.getMessage());
}
}
}, "delay-task-consumer").start();</delaytask>
处理任务取消与内存管理
DelayQueue 不提供主动删除某任务的方法,若需取消未到期任务,常见做法有:
- 在任务内部加 volatile 标志位,
run()前检查是否已被取消 - 维护一个外部 Map
,取消时标记并依赖下次 take()后跳过执行 - 注意:已入队但未到期的任务无法从队列中移除(
remove(Object)效率低且不推荐) - 长期运行系统中,建议定期清理已执行或已取消任务的引用,防止内存泄漏
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











