延迟队列DelayQueue take() 源码分析

2019-08-16 12:04:14来源:博客园 阅读 ()

新老客户大回馈,云服务器低至5折

延迟队列DelayQueue take() 源码分析

延迟队列DelayQueue take() 源码分析

  • 在工作中使用了延迟队列,对其内部的实现很好奇,于是就研究了一下其运行原理,在这里就介绍一下take()方法的源码

1 take()源码 如下所示

public E take() throws InterruptedException {
        // 加锁的一个动作 保证获取数据的安全性
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            for (;;) {
                //peek 方法是去头部数据即第一个数据
                E first = q.peek();
                if (first == null)
                    //说明队列为空 调用condition.await()方法,会使得当前线程释放lock然后加入到等待队列中
                    available.await();
                else {
                    //如果第一个数据不为空 获取消息体的延迟时间(getDelay() 会在消息体内重写 自定义添加延迟时间)
                    long delay = first.getDelay(NANOSECONDS);
                    if (delay <= 0)
                        //如果延迟时间 小于等于0 说明已经达到了延迟时间 调用poll方法返回消息体
                        return q.poll();
                    //如果延迟时间不为空的话 说明还需要等待一段时间 此时重新循环 所以讲frist置为空
                    first = null; 
                    if (leader != null)
                        //这里用到了Leader/Followers模式 有兴趣的话可以去百度一下这个模式
                        //如果leader 不为空 说明已经有线程在监听 即有线程在优先获取队列的首元素
                        //释放当前线程获取的锁 加入到等待队列中 即 当前线程变成了Followers
                        available.await();
                    else {
                        //如果没有leader 说明没有线程在监听(没有线程在优先获取队列的首元素)
                        // 将当前线程置为leader线程 
                        Thread thisThread = Thread.currentThread();
                        leader = thisThread;
                        try {
                            //让当前线程最长等待 delay 时间 等待
                            available.awaitNanos(delay);
                        } finally {
                            //释放leader权限
                            if (leader == thisThread)
                                leader = null;
                        }
                    }
                }
            }
        } finally {
            // 如果leader 为空 且 队列中有数据 说明没有其他线程在在等待 
            if (leader == null && q.peek() != null)
                //唤醒睡眠的线程
                available.signal();
            //释放锁
            lock.unlock();
        }
    }

注意事项:
一开始不明白为什么将frist置为NULL,后面在网上找了相关的资料了解到,如果不讲first置为Null会导致内存泄漏的问题,具体原因如下所示:

  • 如果不将first置为Null,线程A到达,队首元素还没到出列时间,设置线程A = leader
  • 线程B来了因为leader不为空 则会阻塞,后续线程一样。
  • 假如线程A阻塞完毕之后获取列首元素成功出列,这个时候列首元素应该被回收,但是它还被线程B C ...所持有一直不会被回收就导致了内存泄漏(gc一直无法回收frist这个对象)这个情况。

原文链接:https://www.cnblogs.com/karlMa/p/11313330.html
如有疑问请与原作者联系

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:挑战常规--不要这样使用异常

下一篇:反射的深入浅出