DelayQueue 出入队源码分析(摘自java 并发编程的艺术6.3)

DalayQueue 是一个支持延时获取元素的无界阻塞队列,队列使用PriorityQueue来实现,队列中的元素必须实现Delayed接口

运用场景:

  1. 缓存系统的设计,可以保存缓存元素的有效期,一旦能从DelayQueue中获取元素,表示缓存有效期到了

  2. 定时任务调度,使用DelayQueue保存当天将会执行的任务和执行时间,一旦从DelayQueue中获取到任务就开始执行

一个例子:

public class DelayQueueExample {

    static class DelayTask implements Delayed{

        private Date date;

        DelayTask(Date date){
            this.date = date;
        }


        /**
         * 根据时间单位获取该任务被执行的剩余时间,如果取出是负数,将会被DelayQueue取出
         * @param unit
         * @return
         */
        @Override
        public long getDelay(TimeUnit unit) {
            long now = date.getTime() - new Date().getTime();
            return unit.convert(now,TimeUnit.MILLISECONDS);
        }

        /**
         * DelayQueue中的 priorityQueue将会根据这个方法对元素进行排序
         * @param o
         * @return
         */
        @Override
        public int compareTo(Delayed o) {
            if(this == o)
                return 0;
            long d = (getDelay(TimeUnit.NANOSECONDS) -
                    o.getDelay(TimeUnit.NANOSECONDS));
            return (d == 0) ? 0 : ((d < 0) ? -1 : 1);
        }
    }

    public static  void main(String[] args) throws InterruptedException {
        DelayQueue<DelayTask> delayQueue = new DelayQueue();
        for(int i=10;i>0;i--){
            Date date = new Date(new Date().getTime()+i*1000);
            DelayTask delayTask = new DelayTask(date);
            delayQueue.put(delayTask);
        }
        while(!delayQueue.isEmpty()){
            System.out.println(delayQueue.take().date);
        }

    }

}

输出

Thu Sep 13 17:13:43 CST 2018
Thu Sep 13 17:13:44 CST 2018
Thu Sep 13 17:13:45 CST 2018
Thu Sep 13 17:13:46 CST 2018
Thu Sep 13 17:13:47 CST 2018
Thu Sep 13 17:13:48 CST 2018
Thu Sep 13 17:13:49 CST 2018
Thu Sep 13 17:13:50 CST 2018
Thu Sep 13 17:13:51 CST 2018
Thu Sep 13 17:13:52 CST 2018

来看下实现延时阻塞的代码

/**

* Retrieves and removes the head of this queue, waiting if necessary

* until an element with an expired delay is available on this queue.

*

* @return the head of this queue

* @throws InterruptedException {@inheritDoc}

*/

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

                if (leader != null)       //如果发现不为空,说明前面有线程已经在等待取数据了,那么这里直接进入等待

                    available.await();

                else {

                    Thread thisThread = Thread.currentThread();

                    leader = thisThread;         //将自己的线程赋予leader,这样后来的线程都会在上一步阻塞

                    try {

                        available.awaitNanos(delay); //将自己挂起到可以执行任务,再经历一次循环 判断是否可以执行,防止意外唤醒

                    } finally {

                        if (leader == thisThread)

                            leader = null;

                    }

                }

            }

        }

    } finally {

        if (leader == null && q.peek() != null)

            available.signal();                    //唤醒其他线程来取数据

        lock.unlock();

    }

}
/**

* Inserts the specified element into this delay queue.

*

* @param e the element to add

* @return {@code true}

* @throws NullPointerException if the specified element is null

*/

public boolean offer(E e) {

    final ReentrantLock lock = this.lock;

    lock.lock();

    try {

        q.offer(e);        //这里的q是priorityQueue,根据Delayed接口的compareTo来进行优先级排序

        if (q.peek() == e) {

            leader = null;     //如果没有线程来取数据,则唤醒一个

            available.signal();

        }

        return true;

    } finally {

        lock.unlock();

    }

}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值