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 DelayQueue<E extends Delayed> extends AbstractQueue<E>

    implements BlockingQueue<E> {


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

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

    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 取

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值