美文网首页
DelayQueue源码分析

DelayQueue源码分析

作者: 在岁月中远行 | 来源:发表于2023-03-04 20:13 被阅读0次

DelayQueue是一个无界的阻塞队列,它的元素实现了Delayed接口,并且元素只有在delay到期以后才能取出,如果元素没有到期,执行poll方法将返回null。

内部是一个最小堆,堆顶永远是最先"到期"的那个元素,如果堆顶元素没有到期,即使线程发现队列中有元素,也不能将其出队。

DelayQueue需要依赖元素对Delayed接口正确实现,即保证到期时间短的Delayed元素.compareTo(到期长的Delayed元素)<0,这样可以让到期短的Delayed元素排在队列前面。

先看了例子:

我们可以取出元素时间相对于Log.d("tanyonnglin","====================================");分别是传入的时间数字1,2,5,7,9。每次都取队列头部。

成员属性:

1 非公平锁

2 使用PriorityQueue存储元素,是个最小堆。

3 Leader-Follower线程模式中的Leader,它总是等待获取队首。

4 不管哪种线程(Leader或Follower线程)都将阻塞在这个条件队列上,但Follower是无限的阻塞

Leader-Follower:

在队列中的处于队首的Delay元素,由于还没到期,只能暂时等待它到期,这种暂时等待需要用到Condition.awaitNanos。虽然第一个来的线程是可以明确要等待队首元素多久(通过getDelay),但第二个或者后续来的线程就不确定等多久了,其实应该让他们去等待排名第二或以后的元素,但是优先队列事个最小堆,只能确定最小元素,确定不了第二小的元素。

所以就干脆让第二个或后续来的线程无限等待(Condition.await),但我们让第一个线程负责唤醒沉睡在条件队列上的线程。因为第一个线程总是使用Condition.awaitNanos,所以不会造成条件队列上的线程无期限地一直等待,第一个线程总是等待获取堆顶,当它出队成功后,再唤醒后面的线程去获得新堆顶。

上面说的第一个线程其实就是Leader-Follower模式中的Leader了,它总是会以Condition.awaitNanos的方式阻塞,这保证了它不会一直沉睡,而其他线程就是所谓的Follower,当它们检测到Leader的存在时,则可以放心使用Condition.await,反正有Leader线程会唤醒啊。

入队:

1 lock.lock同步

2 添加到优先队列PriorityQueue中

3 如果添加的就是头节点,特殊处理,

说明新元素入队后就称为了堆顶,说明最小元素更新了,这也说明之前的leader(如果存在的话)调用的awaitNanos的不准确了,那么干脆重新清空leader线程,重新唤醒线程重新选出leader。

take():

首先第214行是一个死循环,继续往下看可以发现221行return q.poll()是唯一的出口终止循环,

1: 215-217行 如果队列为空,线程阻塞。

2 :后续代码就是队列不为空情况,存在两者情况。

这里我们来举例分析下:

假设当前队列存在5个元素,delay时间(Delay接口的getDelay()方法返回的值)分别为1,7,5,9,2,单位是TimeUnit.SECONDS也就是秒,这样队列头就是队列时间为2的元素,并且队列头delay时间还没有过期,现在存在3个线程同时调用take方法,来获取队列头,我们来分析这个过程。

1 假设线程1拿到了lock锁,执行到220行时,发现delay>0,肯定是无法立即返回的。

2 执行到223行时,判断leader是否为Null,此时leader是为Null的。

3 将当前线程也就是线程1赋给leader,然后线程阻塞delay,释放锁。

4 此时线程2如果拿到了锁,发现leader不为null,执行224行代码。立即阻塞,然后释放锁。

5 线程3同线程2一样

6 当线程1阻塞时间到达后,会执行231-233行代码,在死循环中再次执行到220行代码时,此时delay<=0,退出循环,在返回队列头之前,会执行finally块中通知其他线程唤醒

7 假设线程2被唤醒,那么步骤和线程1一样的类似。

这里的leader线程代表着等待队列头时间到达的线程,leader线程永远等待队列头的超时时间,除了leader线程的其他线程均无限等待直到收到唤醒通知,才有机会成为leader线程。

结论:

DelayQueue类特性,在生产者-消费者模型中,只有在特定元素过期后才能将他们消费,另一个用途在于线程池,提交任务以后延时执行。

相关文章

  • DelayQueue源码分析

    阻塞队列系列 ArrayBlockingQueue源码解析 LinkedBlockingQueue源码解析 Syn...

  • DelayQueue源码分析

    延迟队列:往队列中放入的元素具有一定的延迟时间,延迟时间到期后,take或者poll方法才能获取到这些元素。先看以...

  • DelayQueue源码分析

    DelayQueue是一个无界的阻塞队列,它的元素实现了Delayed接口,并且元素只有在delay到期以后才能取...

  • DelayQueue之源码分析

    本文将会对DelayQueue做一个简单的介绍,并提供部分源码的分析。 DelayQueue的特性基本上由Bloc...

  • 阻塞队列 — DelayQueue源码分析

    点赞再看,养成习惯,公众号搜一搜【一角钱技术[https://p3-juejin.byteimg.com/tos-...

  • DelayQueue源码学习

    DelayQueue源码学习 DelayQueue是一个提供过期时间的队列,只返回消耗完等待时间的元素,暂时还没发...

  • 读源码DelayQueue-Leader-Follower模式

    java.util.concurrent.DelayQueue采用了Leader-Follower模式,结合源码理...

  • java源码-DelayQueue

    开篇  DelayedQueue是一个用来延时处理的队列,delayQueue其实就是在每次往优先级队列中添加元素...

  • DelayQueue 源码解析

    如果不想在世界上虚度一生,那就要学习一辈子。 1 整体设计 DelayQueue 延迟队列底层使用的是锁的能力,比...

  • JUC源码分析-集合篇(十):DelayQueue

    DelayQueue 是一个无界延时阻塞队列,元素顺序按照过期时间排序,通过显式锁 ReentrantLock 保...

网友评论

      本文标题:DelayQueue源码分析

      本文链接:https://www.haomeiwen.com/subject/scmyldtx.html