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,长时间大量阻塞线程会造成内存泄漏

相关推荐
Wang's Blog1 小时前
JavaWeb快速入门: Filter与Listener核心详解
java·filter·listener
han_hanker1 小时前
重新认识枚举enum
开发语言
zfoo-framework1 小时前
mongodb性能调优 1.从慢查询分析索引 2.掌握索引最左匹配原则!!!
java
AIGS0011 小时前
企业AI落地:从RAG到AgentRAG的技术跃迁
java·人工智能·人工智能ai大模型应用
鹏易灵2 小时前
C++——8.移动语义初探(移动构造、移动赋值)
开发语言·c++·php
2601_962341302 小时前
计算机毕业设计之jsp考研在线复习平台
java·大数据·开发语言·hadoop·python·考研·课程设计
凯瑟琳.奥古斯特2 小时前
力扣1008:前序重建BST
开发语言·c++·算法·leetcode·职场和发展
huangjiazhi_2 小时前
纯C++实现ini文件操作
java·后端·spring