delayqueue 的 poll() 方法天然非阻塞:队空或队首未到期时返回 null,仅到期时移除并返回元素;正确实现 delayed 接口(基于 system.nanotime() 计算纳秒级延迟)是前提。

DelayQueue 是 Java 并发包中一个无界阻塞队列,内部基于优先队列(最小堆)实现,元素必须实现 Delayed 接口。它的 poll() 方法本身是非阻塞的 —— 如果队首元素尚未到期,它会立即返回 null;只有 take() 才是阻塞的。
关键点:poll() 本身就是非阻塞的,无需额外配置或包装。 它天然满足“只取已到期任务、不等待、不阻塞”的需求。
poll() 的行为逻辑
调用 delayQueue.poll() 时:
- 若队列为空 → 返回 null
- 若队首元素(即最早到期的)尚未到期(getDelay(TimeUnit.NANOSECONDS) > 0)→ 返回 null
- 若队首元素已到期(getDelay(...) ≤ 0)→ 移除并返回该元素
确保 Delayed 实现正确
这是非阻塞获取生效的前提。常见错误会导致 getDelay() 始终返回正数,使 poll() 永远不返回任务:
- getDelay() 必须以纳秒为单位,且基于 System.nanoTime()(而非 currentTimeMillis)计算剩余延迟
- 示例正确写法:
public long getDelay(TimeUnit unit) {
long remaining = triggerTime - System.nanoTime();
return unit.convert(remaining, TimeUnit.NANOSECONDS);
}
典型轮询模式(推荐轻量使用)
在业务线程中周期性调用 poll(),处理所有当前可执行任务:
- 用 while 循环反复 poll(),直到返回 null(说明暂无到期任务)
- 避免 sleep 空等,但注意控制轮询频率,防止 CPU 空转
- 示例:
while (!Thread.interrupted()) {
Runnable task = delayQueue.poll();
if (task == null) {
Thread.sleep(10); // 小间隔休眠,降低开销
continue;
}
task.run(); // 执行到期任务
}
与 drainTo() 配合批量获取
如果一次想取出多个已到期任务(提升吞吐),可用 drainTo:
- drainTo(Collection super E> c) 会把所有已到期任务移出并加入集合,非阻塞、原子
- 注意:它不会等待新任务到期,只处理“此刻已满足条件”的元素
- 适合定时批量调度场景,如每秒汇总执行一批延时消息










