前言

DelayQueue是BlockingQueue接口的实现类,它是带有延时功能的无界阻塞队列,意思就是每个元素都有过期时间,当从队列获取元素时,只有过期元素才会出队列。队列的头部元素是最快要过期的元素。

实现原理

先来看看它的类结构:

public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
    implements BlockingQueue<E> {
}

可以看到,存放的元素必须实现了Delayed接口,它的定义如下:

public interface Delayed extends Comparable<Delayed> {
  // 返回的值代表还剩多长时间过期
  long getDelay(TimeUnit unit);
}

再来看关键属性:

// 操作队列的互斥锁
private final transient ReentrantLock lock = new ReentrantLock();
// 用优先级队列来存放元素,因此上面的Delayed接口要实现Comparable接口
private final PriorityQueue<E> q = new PriorityQueue<E>();
//  基于Leader-Follower模式的变体,用于减少不必要的线程等待
private Thread leader = null;
// 互斥锁绑定的条件,目的是为了实现线程间的同步
private final Condition available = lock.newCondition();

再来看重要方法:
offer()

public boolean offer(E e) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
         // 调用优先级队列q,让元素e入队
         q.offer(e);
         // 由于是优先级队列,头部元素不一定是刚插入的元素e,
         // 如果是e的话,说明e就是最先过期的,
         // 那么重置leader为null,唤醒某个等待在available条件上的take线程,
         // 告知它队列头部有元素可用了
         if (q.peek() == e) {
             leader = null;
             available.signal();
         }
         return true;
    } finally {
        lock.unlock();
    }
}

poll()

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();
    }
}

take()

public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        // 无限循环尝试移除元素
        for (;;) {
            E first = q.peek();
            // 队列为空,则阻塞等待
            if (first == null)
                available.await();
            else {
                // 队列不为空,检查头部元素是否过期
                long delay = first.getDelay(NANOSECONDS);
                // 过期了直接返回
                if (delay <= 0)
                    return q.poll();
                first = null; // don't retain ref while waiting
                // 未过期且leader不为空,表示有其他线程也在take()操作,
                // 当前线程则成为follower线程,在条件队列里阻塞等待
                if (leader != null)
                    available.await();
                else {
                    // leader为空,则将当前线程置为leader
                    Thread thisThread = Thread.currentThread();
                    leader = thisThread;
                    try {
                        // 等待头部元素的剩余过期时长delay
                        // 等待过程中会释放锁lock,其他线程可offer或take操作
                        available.awaitNanos(delay);
                    } finally {
                        // 线程苏醒后重置leader为null,因为下次循环first元素过期时间就到了,可以返回头部元素
                        if (leader == thisThread)
                            leader = null;
                    }
                }
            }
        }
    } finally {
        // take操作await过程中,其他线程执行了offer操作,可唤醒其他等待在available变量上的take操作线程
        // 表示队列头部元素不为空了
        if (leader == null && q.peek() != null)
            available.signal();
        lock.unlock();
  }
}

参考资料:
《Java并发编程之美》


小强大人
34 声望4 粉丝