JAVA并发编程:阻塞队列-DelayQueue

生活

如果第一次你没有成功,那么称之为1.0版,继续加油。

DelayQueue的成员组成

今天来学习延时队列,这个玩意儿也是非常重要,在定时器上有用到。
首先简单了解下,延时队列就是让指定的数据再指定的时候以后出队,也就是按照时间排序,因此它的核心确实是使用了昨天看的优先队列。
下面先来看看DelayQueue的成员

//可重入锁
    private transient final ReentrantLock lock = new ReentrantLock();
    //优先队列
    private final PriorityQueue<E> q = new PriorityQueue<E>();
    //头线程。如果不为空,说明之前的线程还没等到数据,那后面的就继续等着吧
    private Thread leader = null;
    //条件,是否允许,到达指定时间才允许
    private final Condition available = lock.newCondition();

DelayQueue创建

 public DelayQueue() {}

//这里的addAll最后调用到offer,本质是遍历集合里的元素,
然后一个一个塞到优先队列尾部,这样效率不是不高吗?
很奇怪,为啥没有调用优先队列直接传入集合的构造器,
先组成一个数组,在headify..?

//另外需要注意的集合的元素必须实现Delay接口

 public DelayQueue(Collection<? extends E> c) {
        this.addAll(c);
    }


//可以看到这个接口的父接口是Comparable,这样就满足了塞入优先队列的条件
public interface Delayed extends Comparable<Delayed> {
   long getDelay(TimeUnit unit);
}
    

DelayQueue入队

入队最终就调用了offer,不存在阻塞入队,因为使用了优先队列,
该队列本身实现了扩容机制,不存在容量不足的情况。

 public boolean offer(E e) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
        //调用优先队列入队
            q.offer(e);
            //如果根节点就是e,说明当前这个节点就是最快要出队的,唤醒等待的线程即可
            if (q.peek() == e) {
                leader = null;
                available.signal();
            }
            return true;
        } finally {
            lock.unlock();
        }
    }

DelayQueue出队

非阻塞出队:

public E poll() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            E first = q.peek();
            //如果根节点是空或者还不到时间,就return null
            if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0)
                return null;
            else
                return q.poll();
        } 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)
                        return q.poll();
                        //如果时间没到且有线程在前面就等待
                    else if (leader != null)
                    /
                        available.await();
                    else {
                    //如果时间没到,且前面没有线程,就设置自己为leader,最近一个到时间的由我获取
                        Thread thisThread = Thread.currentThread();
                        leader = thisThread;
                        try {
                        //等待指定时间,唤醒后清空自己的leader去取数据
                            available.awaitNanos(delay);
                        } finally {
                            if (leader == thisThread)
                                leader = null;
                        }
                    }
                }
            }
        } finally {
        //如果我已经清空leader并且后面还有数据,唤醒后面等待的线程
            if (leader == null && q.peek() != null)
                available.signal();
            lock.unlock();
        }
    }

猜你喜欢

转载自blog.csdn.net/qq_28605513/article/details/84704436