DelayQueue 源码分析

本文参考了 https://www.cnblogs.com/myseries/p/10944211.html 

  

一、DelayQueue是什么

  DelayQueue是一个无界的BlockingQueue,用于放置实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取走。这种队列是有序的,即队头对象的延迟到期时间最长。注意:不能将null元素放置到这种队列中。

二、DelayQueue能做什么

 1. 淘宝订单业务:下单之后如果三十分钟之内没有付款就自动取消订单。 
 2. 饿了吗订餐通知:下单成功后60s之后给用户发送短信通知。

 3.关闭空闲连接。服务器中,有很多客户端的连接,空闲一段时间之后需要关闭之。

 4.缓存。缓存中的对象,超过了空闲时间,需要从缓存中移出。

 5.任务超时处理。在网络协议滑动窗口请求应答式交互时,处理超时未响应的请求等。

三  代码示例

  DelayQueue只能添加(offer/put/add)实现了Delayed接口的对象,意思是说我们不能想往DelayQueue里添加什么就添加什么,不能添加int、也不能添加String进去,必须添加我们自己的实现了Delayed接口的类的对象

class MyDelayedTask implements Delayed{

    private String name ;
    private long start = System.currentTimeMillis();
    private long time ;

    public MyDelayedTask(String name,long time) {
        this.name = name;
        this.time = time;
    }

    /**
     * 需要实现的接口,获得延迟时间   用过期时间-当前时间
     * @param unit
     * @return
     */
    @Override
    public long getDelay(TimeUnit unit) {
        return unit.convert((start+time) - System.currentTimeMillis(),TimeUnit.MILLISECONDS);
    }

    /**
     * 用于延迟队列内部比较排序   当前时间的延迟时间 - 比较对象的延迟时间
     * @param o
     * @return
     */
    @Override
    public int compareTo(Delayed o) {
        MyDelayedTask o1 = (MyDelayedTask) o;
        return (int) (this.getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS));//比较方法必须要和getDelay的逻辑一直,就是说时间越小的优先级也是越高的
    }

    @Override
    public String toString() {
        return "MyDelayedTask{" +
                "name='" + name + '\'' +
                ", time=" + time +
                '}';
    }
}

  

private static DelayQueue delayQueue  = new DelayQueue();
    public static void main(String[] args) throws InterruptedException {

        new Thread(new Runnable() {
            @Override
            public void run() {

                delayQueue.offer(new MyDelayedTask("task1",10000));
                delayQueue.offer(new MyDelayedTask("task2",3900));
                delayQueue.offer(new MyDelayedTask("task3",1900));
                delayQueue.offer(new MyDelayedTask("task4",5900));
                delayQueue.offer(new MyDelayedTask("task5",6900));
                delayQueue.offer(new MyDelayedTask("task6",7900));
                delayQueue.offer(new MyDelayedTask("task7",4900));

            }
        }).start();

        while (true) {
            Delayed take = delayQueue.take();
            System.out.println(take);
        }
    }

四  源码分析

  关键的成员变量

    private final transient ReentrantLock lock = new ReentrantLock();//同步工具
    private final PriorityQueue<E> q = new PriorityQueue<E>();//内部使用了PriorityQueue
    private Thread leader = null;//第一个等待的线程
    private final Condition available = lock.newCondition();//负责阻塞

  leader是第一个等待的线程,只凭这句话不太好理解,下文分析poll的时候会进一步讲解

  入队

public boolean offer(E e) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            q.offer(e);//调用底层的offer方法
            if (q.peek() == e) {//如果入队成功了
                leader = null;//leader置空
                available.signal();//通知condition
            }
            return true;
        } finally {
            lock.unlock();
        }
    }

  出队 

public E poll() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            E first = q.peek();
            if (first == null || first.getDelay(NANOSECONDS) > 0)//如果delay大于0 就是说还没到时间,那就直接返回null
                return null;
            else
                return q.poll();//返回底层的poll
        } finally {
            lock.unlock();
        }
    }

  代码并不复杂,主要逻辑就是判断delay时间到没到,没到的话直接返回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);//停止timeout指定的时间
                } else {
                    long delay = first.getDelay(NANOSECONDS);
                    if (delay <= 0)
                        return q.poll();//如果delay小于0,直接返回值
                    if (nanos <= 0)
                        return null;//拉取等待时间小于等于0,就返回null
                    first = null; // don't retain ref while waiting 这里我理解是为了释放强引用
                    if (nanos < delay || leader != null)//leader是第一个等待的线程,也就是说如果delay到了,优先给他。nanos < delay 说明等待到了delay还到不了,那就阻塞等待
                        nanos = available.awaitNanos(nanos);//注意这里的返回值,返回值代表的是awaitNanos等待的剩余时间,
              比如如果没有中断那么这个值大概<=0,如果等待被中断那就是0<nanos< 之前的nanos,leader !=null 那么下一次自旋该线程就有机会拿到数据了
else { Thread thisThread = Thread.currentThread();//上面的if没拦住的话说明delay<nanos同时leader==null,那也就是说在等待期间是能够拿到数据的 leader = thisThread;//优先把数据给他 try { long timeLeft = available.awaitNanos(delay);//等待delay这么久,不出异常就应该拿到数据了 timeLeft大概 <=0 nanos -= delay - timeLeft;//剩余的等待时间 } finally { if (leader == thisThread) leader = null; } } } } } finally { if (leader == null && q.peek() != null) available.signal(); lock.unlock(); } }

 

posted on 2020-12-01 15:41  MaXianZhe  阅读(71)  评论(0)    收藏  举报

导航