DelayQueue源码学习

DelayQueue源码学习

DelayQueue是一个提供过期时间的队列,只返回消耗完等待时间的元素,暂时还没发现应用场景。。。。
DelayQueue实现了BlockingQueue接口,所以是支持阻塞操作的


  • 首先想要入队的元素必须实现Delayed接口,先来看Delayed接口:
//继承了Comparable接口
public interface Delayed extends Comparable<Delayed> {

    /**
     * Returns the remaining delay associated with this object, in the
     * given time unit.
     *
     * @param unit the time unit
     * @return the remaining delay; zero or negative values indicate
     * that the delay has already elapsed
     *返回小于等于0表示延迟到期了
     */
    long getDelay(TimeUnit unit);
}

下面是一个实现了Delayed接口的类:

class DelayBean implements Delayed{
    private long time;
    
    

    @Override
    public int compareTo(Delayed o) {
        // 大于返回1,小于返回-1,等于返回0
        return this.getDelay(TimeUnit.NANOSECONDS) > o.getDelay(TimeUnit.NANOSECONDS) ? 1 : 
            (this.getDelay(TimeUnit.NANOSECONDS) < o.getDelay(TimeUnit.NANOSECONDS) ? -1 : 0);
    }

    //返回成员变量的时间和当前时间的差
    @Override
    public long getDelay(TimeUnit unit) {
        // TODO Auto-generated method stub
        return unit.convert(time - System.nanoTime(), TimeUnit.NANOSECONDS);
    }

    public long getTime() {
        return time;
    }

    public void setTime(int time) {
        this.time = time;
    }
    
}


  • 接下来看一下DelayQueue的成员变量:
    //重入锁
    private final transient ReentrantLock lock = new ReentrantLock();
    //聚合了一个优先级队列来实现保存元素
    private final PriorityQueue<E> q = new PriorityQueue<E>();
    //用于减少定时等待,优化性能,Leader-Follower模式见,见(这里)[http://www.cs.wustl.edu/~schmidt/POSA/POSA2/]
    private Thread leader = null;
    
    private final Condition available = lock.newCondition();

ps:有关PriorityQueue见这里,由于是聚合了一个PriorityQueue来保存优先级队列,所以DelayQueue类的主要精力是放在如何去实现这个延时的功能的


  • 入队系列方法

由于聚合的PriorityQueue是一个基于数组实现的无界队列,所以这里的offer, put方法都不会阻塞,调用这些方法最终都是到这个方法:

 public boolean offer(E e) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            q.offer(e);
            //这里对leader的操作有点迷,先看完下面的take(),poll()方法再回来看
            if (q.peek() == e) {
                leader = null;
                available.signal();
            }
            //看完了take(),和poll()方法,回来继续看这个,如果leader处于非null的情况
            //说明那个当前这个leader指向的线程是可以在结束休眠的时候获取到超时结束的元素的
            //先假设为offer以前堆顶元素已经被某个线程预定了可以在结束超时出队
            //这个时候新加的元素直接到了堆顶,说明这个时候队列里出现了两个满足超时等待
            //的元素可以出队列,这个时候把leader置为null,等于告诉后来者的线程
            //你也可以把自己置为leader,预定下一个出队的元素
            return true;
        } finally {
            lock.unlock();
        }
    }

  • poll()方法
/**
*发现一个有趣的地方,获取锁后直接从PrirorityQueue(下称pq)里去检索队头的元素
*然后去看他是否满足条件,不满足直接返回null,反之才正从pq里poll出来
*这个条件就是延迟等待是否到达的条件
**/
public E poll() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            E first = q.peek();
            if (first == null || first.getDelay(NANOSECONDS) > 0)
                return null;
            else
                return q.poll();
        } finally {
            lock.unlock();
        }
    }

  • 阻塞等待的take()方法
 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
                    /**
                    *这里体现了leader的妙用,如果leader不为null,说明有其他线程在等待着出队
                    *直接调用await(),而不是awaitNanos();
                    **/
                    if (leader != null)
                        available.await();
                    else {
                        //如果没有线程在等待获取,调用awaitNanos()等待过期时间后就退出等待,再次循环尝试获取
                        //await()和awaitNanos()结合使用,提高性能
                        Thread thisThread = Thread.currentThread();
                        leader = thisThread;
                        try {
                            available.awaitNanos(delay);
                        } finally {
                            if (leader == thisThread)
                                leader = null;
                        }
                    }
                }
            }
        } finally {
            if (leader == null && q.peek() != null)//最后如果peek()不为null,唤醒其他等待的线程
                available.signal();
            lock.unlock();
        }
    }

  • 超时等待的poll(TimeUnit timeUnit, long timeout)方法:
 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(NANOSECONDS);
                    if (delay <= 0)
                        return q.poll();
                    if (nanos <= 0)
                        return null;
                    first = null; // don't retain ref while waiting
                    
                    /**
                    *两段注释之间的代码解释了成员变量Thread leader的作用
                    *这里的leader作用和take的方法有点不同
                    **/
                    if (nanos < delay || leader != null)
            //这里是nanos < delay 说明等待时间短于最快过期的那个元素,
            //如果没有新元素入队的话,这次poll是一定返回null的,
            //所以让当前线程等待该等待的时间

            //leader != null,说明有其他线程在等待获取元素,就是当前这个满足条件的
            //元素已经被其他线程预定了,你是拿不到的,该干嘛干嘛去,等待该等待的时间就行
                        nanos = available.awaitNanos(nanos);
                    else {
                        Thread thisThread = Thread.currentThread();
                        leader = thisThread;
                        try {
                            //如果nanos > delay 表示等待delay的时间是有可能获取到出队元素
                            //并且把当前线程置为leader,等于告诉其他线程,这个出队名额已经被我预定了
                            long timeLeft = available.awaitNanos(delay);
                            nanos -= delay - timeLeft;
                        } finally {
                            //结束一次等待后把leader置为null
                            if (leader == thisThread)
                                leader = null;
                        }
                    }
                    /**
                    *这个时候再回去看看offer里对leader的相关代码的含义
                    **/
                }
            }
        } finally {
            if (leader == null && q.peek() != null)
                available.signal();
            lock.unlock();
        }
    }

所以在poll()方法里leader的作用是让一些无法获取出队超时元素的线程等待该等待的时间然后返回null,让可以获取到出队超时出队元素的线程更好地获取到出队元素。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容

  • 本文将会对DelayQueue做一个简单的介绍,并提供部分源码的分析。 DelayQueue的特性基本上由Bloc...
    逍遥jc阅读 1,404评论 2 2
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,494评论 18 139
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,493评论 18 399
  • 1. 块级元素和行内元素分别有哪些?动手测试并列出4条以上的特性区别 特性区别 块级元素可以包含行内元素和块级元素...
    饥人谷_哈噜噜阅读 366评论 0 0
  • -1- 10月1日,对林典来说是个特别重要的日子。 这一天,他一定会推掉所有的工作和应酬,一个跑到半山腰上静静地发...
    共央君阅读 2,314评论 36 79