DelayQueue 源码

调用 demo

java 复制代码
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

class Task implements Delayed {
    private long expire;
    private String name;

    public Task(String name, long delay, TimeUnit unit) {
        this.name = name;
        this.expire = System.currentTimeMillis() + unit.toMillis(delay);
    }

    @Override
    public long getDelay(TimeUnit unit) {
        long remain = expire - System.currentTimeMillis();
        return unit.convert(remain, TimeUnit.MILLISECONDS);
    }

    @Override
    public int compareTo(Delayed o) {
        return Long.compare(this.getDelay(TimeUnit.MILLISECONDS), o.getDelay(TimeUnit.MILLISECONDS));
    }

    @Override
    public String toString() { return name; }
}

public class DelayQueueMain {
    public static void main(String[] args) throws InterruptedException {
        DelayQueue<Task> queue = new DelayQueue<>();
        queue.put(new Task("1s任务", 1, TimeUnit.SECONDS));
        queue.put(new Task("3s任务", 3, TimeUnit.SECONDS));
        queue.put(new Task("2s任务", 2, TimeUnit.SECONDS));

        for (int i = 0; i < 3; i++) {
            Task t = queue.take();
            // 取出到期:1s任务
			// 取出到期:2s任务
			// 取出到期:3s任务
            System.out.println("取出到期:" + t);
        }
    }
}

成员变量

java 复制代码
// 底层最小堆,存储延迟元素
private final PriorityQueue<E> q = new PriorityQueue<E>();

// 全局独占锁,所有读写加锁
private final ReentrantLock lock = new ReentrantLock();

// 等待条件:无到期元素/队空时阻塞take线程
private final Condition available = lock.newCondition();

// Leader-Follower 优化标记
private Thread leader = null;

构造方法

java 复制代码
// ========== 无参构造器 main new 时执行 ==========
public DelayQueue() {}

// 带集合构造器(main未使用,附原版)
public DelayQueue(Collection<? extends E> c) {
    this.addAll(c);
}

入队方法

put

java 复制代码
public void put(E e) {
    offer(e);
}

add

java 复制代码
public boolean add(E e) {
    return offer(e);
}

offer(超时)

java 复制代码
public boolean offer(E e, long timeout, TimeUnit unit) {
    return offer(e);
}

offer

java 复制代码
public boolean offer(E e) {
     final ReentrantLock lock = this.lock;
     lock.lock();
     try {
         q.offer(e); // 放入堆,自动上浮排序
         // 如果新元素是堆顶,清空 leader 并唤醒 1 个线程
         if (q.peek() == e) {
             leader = null;
             available.signal();
         }
         return true;
     } finally {
         lock.unlock();
     }
}

出队方法

take

java 复制代码
public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly(); // 可中断锁,响应线程interrupt
    try {
        for (;;) { // 死循环自旋校验队首
            E first = q.peek();
            // 分支1:队列为空,无限阻塞等待
            if (first == null)
                available.await();
            else {
                long delay = first.getDelay(NANOSECONDS);
                // 分支2:队首已到期,直接弹出返回
                if (delay <= 0)
                    return q.poll();
                // 未到期,释放引用防止内存泄漏
                first = null;
                // 分支3:已有 leader 线程,当前线程永久阻塞
                if (leader != null)
                    available.await();
                else {
                    // 无leader,当前线程成为 leader,定时等待剩余延迟
                    Thread thisThread = Thread.currentThread();
                    leader = thisThread;
                    try {
                        available.awaitNanos(delay);
                    } finally {
                        // 等待结束,清空leader标记
                        if (leader == thisThread)
                            leader = null;
                    }
                }
            }
        }
    } finally {
        // 释放锁前:如果无 leader 且队列有元素,唤醒一个等待线程
        if (leader == null && q.peek() != null)
            available.signal();
        lock.unlock();
    }
}

poll

java 复制代码
public E poll() {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        E first = q.peek();
        // 空 或 未到期 返回null
        if (first == null || first.getDelay(NANOSECONDS) > 0)
            return null;
        else
            return q.poll();
    } finally {
        lock.unlock();
    }
}

总结

  1. 无界阻塞队列:put/offer 永不阻塞,无容量限制
  2. 底层最小堆:队首一定是最快到期元素
  3. 线程安全:全局一把 ReentrantLock ,所有操作都会上锁
  4. leader 等待优化:减少多线程定时竞争,提升并发性能
  5. 仅到期可出队:take/poll 不会返回未到期元素,未到期线程阻塞

相关考点

first = null 为什么可以防止内存泄漏

不执行 first = null 时的引用关系如下:

  • 阻塞线程的局部变量 first 引用堆顶延迟任务对象 Delayed Task
  • DelayQueue 内部 PriorityQueue 也引用同一个 Task

当线程执行到 available.await() / awaitNanos() 阻塞时:

线程挂起、长时间休眠,线程栈帧不会销毁,局部变量 first 会一直存活

双重强引用导致 GC 无法回收

队列里本来就有一份引用,再加线程局部变量的强引用

当这个任务到期被别的线程 poll 取出、从堆里删掉,当前阻塞线程的变量 first 依旧死死持有该对象

只要线程没被唤醒、栈帧没退出,Task 及其关联大内存就无法被 GC,长时间大量阻塞线程会造成内存泄漏

相关推荐
白帽攻防录10 小时前
SRC 挖洞:PostScript 打印机驱动双 RCE,打印服务器怎么变成内网跳板
开发语言·网络安全·php·内网安全
聚美智数10 小时前
智能结构化识别-图片结构解析-图像结构化解析-照片结构化解析
android·java·数据库
十年Java程序媛10 小时前
Java枚举Enum完整教程|替代静态常量,开发与面试要点
java·开发语言·spring boot·后端·面试
我是人✓10 小时前
SpringBoot静态资源映射揭秘
java·spring boot·后端
2603_9651481110 小时前
电商选品调研:通过关键词搜索API获取市场热销商品,结合商品详情API分析竞品卖点与定价
开发语言·数据库·人工智能·python·信息可视化
土豆.exe11 小时前
nf_tables 的 verdict 被读了两次:一个十年未闭合的位域错配
java·数据库·网络安全·缓存
xixiaoyunya11 小时前
Python 异步编程入门:用 asyncio 轻松实现并发任务
开发语言·python·php
学逆向的11 小时前
C 循环结构的执行效率分析
c语言·开发语言·数据结构·网络安全
小坏讲微服务12 小时前
刚发布一个月的JDK 27,为什么我建议你立刻升级?
java·开发语言·spring boot·spring·架构·求职招聘·springboot4
kunicoding12 小时前
多线程编程核心概念详解:从线程基础到并发安全
java·jvm·安全