DelayQueue简介
一个支持延时获取元素的无界阻塞队列。里面的元素全部都是“可延期”的元素,列头的元素是最先“到期”的元素,如果队列里面没有元素到期,是不能从列头获取元素的,哪怕有元素也不行。也就是说只有在延迟期到时才能够从队列中取元素。

DelayQueue使用场景
1.清掉缓存中超时的数据
2.超时任务处理 

DelayQueue特性
1.使用重入锁ReentrantLock和available来控制并发。
2.以优先级队列PriorityQueue为存储容器,元素必须实现Delayed接口,Delayed接口继承自Comperable接口,实现Delayed接口的类还必须要定义一个compareTo方法,该方法提供与此接口的getDelay方法一致的排序。
3.最先过期的元素优先级最高,只有元素达到指定的延迟时间了,才能从队列中获取。
4.因为是无界队列,往队列插入元素永远不会发生阻塞,获取元素队列为空时会发生阻塞,所有元素的延迟时间未到也会发生阻塞,指定超时时间。
5.leader线程:用于优化内部阻塞通知的线程,第一个阻塞等待的线程。

/**
     * 还剩多少时间到期
     */
    @Override
    public long getDelay(TimeUnit unit) {
        return unit.convert(this.expire - System.currentTimeMillis(),TimeUnit.MILLISECONDS);                       
    }

    /**
     * 队列优先级规则
     */
    @Override
    public int compareTo(Delayed o) {
        return (int) (this.getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS));
    }
public boolean offer(E e) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            q.offer(e);
            // q.peek() == e有两种情况:1队列只有一个元素e,2.元素e的优先级最高
            if (q.peek() == e) {
                leader = null;
                // 当首次入队元素时,需要唤醒一个出队线程,因为此时可能已有出队线程在空队列上等待了,如果不唤醒,需要等待下一个线程来唤醒
                // 或者无限期等待下去
                available.signal();
            }
            return true;
        } finally {
            lock.unlock();
        }
    }

    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; // 防止多线程拥有first引用对象,导致无法gc出队元素,造成内存泄漏
                    if (leader != null)
                        available.await();
                    else {
                        // DelayQueue不会让所有的线程都做无限期等待,而是用leader保存了第一个尝试出队的线程,等待时间是对首元素的剩余有效期(性能提升)
                        // 只要leader线程获取到了元素,在q.poll返回前,执行leader == null && q.peek() != null的signal
                        // 会唤醒等待队列中的一个线程充当leader或者直接返回元素唤醒下一个
                        Thread thisThread = Thread.currentThread();
                        leader = thisThread;
                        try {
                            available.awaitNanos(delay);
                        } finally {
                            if (leader == thisThread)
                                leader = null;
                        }
                    }
                }
            }
        } finally {
            if (leader == null && q.peek() != null)
                // leader为空且队列有元素,唤醒一个等待队列中的元素
                available.signal();
            lock.unlock();
        }
    }