DelayQueue源码解析

    Delayed 元素的一个无界阻塞队列,只有在延迟期满时才能从中提取元素。该队列的头部 是延迟期满后保存时间最长的 Delayed 元素。如果延迟都还没有期满,则队列没有头部,并且 poll 将返回 null。当一个元素的 getDelay(TimeUnit.NANOSECONDS) 方法返回一个小于等于 0 的值时,将发生到期。即使无法使用 take 或 poll 移除未到期的元素。

            注意:为了具有调用行为,存放到DelayDeque的元素必须继承Delayed接口。Delayed接口使对象成为延迟对象,它使存放在DelayQueue类中的对象具有了激活日期。该接口强制执行下列两个方法。

  • CompareTo(Delayed o):Delayed接口继承了Comparable接口,因此有了这个方法。
  • getDelay(TimeUnit unit):这个方法返回到激活日期的剩余时间,时间单位由单位参数指定。

概述:添加到队列的元素实现了Delayed接口的比较方法和获得延迟时间方法,实际添加元素到了PriorityQueue队列并根据优先级进行了排序。take在获取队列元素时判断元素的延迟时间是否到期,未到期进行阻塞,到期才能获取元素。

public class DelayQueueDelayed> extends AbstractQueue

    implements BlockingQueue {


    private transient final ReentrantLock lock = new ReentrantLock();//全局锁
    private transient final Condition available = lock.newCondition();//锁监视器

    private final PriorityQueue q = new PriorityQueue();//存放元素的队列,采用了优先级队列

    public boolean add(E e) {
        return offer(e);

    }

    public boolean offer(E e) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            E first = q.peek();//获取但不移除队列元素
            q.offer(e);//添加元素到队列,队列会根据元素的比较方法排序
            if (first == null || e.compareTo(first) < 0)//如果原来队列为空或者现添加元素的比头元素优先,把阻塞队列激活
                available.signalAll();
            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(TimeUnit.NANOSECONDS);//获取元素的延迟时间
                    if (delay > 0) {//如果延迟时间>0说明还未到期,进行wait delay时间段后,自动激活
                        long tl = available.awaitNanos(delay);
                    } else {//延迟时间<=0,说明已经到期,进行出队
                        E x = q.poll();
                        assert x != null;
                        if (q.size() != 0)
                            available.signalAll(); // wake up other takers
                        return x;


                    }
                }
            }
        } finally {
            lock.unlock();
        }

    }
总结:take阻塞原理:通过peek()获得队列头,进行判断队列为空进行阻塞,如果队列不为空,判断队列头的getDelay()延迟时间是否<0如果小于说明到期进行poll取出队列,如果延迟时间>0说明未到期,通过awaitNanos(delay)阻塞只到延迟时间。

//和take方法相同,只是在队列为空时,返回null不进行阻塞。

    public E poll(long timeout, TimeUnit unit) throws InterruptedException {
        long nanos = unit.toNanos(timeout);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            for (;;) {
                E first = q.peek();
                if (first == null) {
                    if (nanos <= 0)
                        return null;
                    else
                        nanos = available.awaitNanos(nanos);
                } else {
                    long delay = first.getDelay(TimeUnit.NANOSECONDS);
                    if (delay > 0) {
                        if (nanos <= 0)
                            return null;
                        if (delay > nanos)
                            delay = nanos;
                        long timeLeft = available.awaitNanos(delay);
                        nanos -= delay - timeLeft;
                    } else {
                        E x = q.poll();
                        assert x != null;
                        if (q.size() != 0)
                            available.signalAll();
                        return x;
                    }
                }
            }
        } finally {
            lock.unlock();
        }

    }

take 取

你可能感兴趣的:(JDK源码分析)