探究延时队列 DelayQueue 的实现原理
如果不想在世界上虚度一生,那就要学习一辈子。
——高尔基
Q:小伙子,讲讲对DelayQueue的认识?
> 继续将Oracle官方说明作为开场白即可。
延迟元素的无界阻塞队列,在该队列中,仅当元素的延迟到期时才可以使用它。队首是 Delayed 元素,其延迟到期的时间最长。如果没有延迟已经过期,就没有head,poll将返回null。
当元素的getDelay(TimeUnit.NANOSECONDS)方法返回的值小于或等于零时,就会发生过期。即使未到期的元素无法使用take或poll删除,它们也被视为普通的元素。 例如,size方法返回过期和未过期元素的计数.
此队列不允许null元素。该类及其迭代器实现集合和迭代器接口的所有可选方法。方法Iterator()中提供的迭代器不能保证以任何特定的顺序遍历DelayQueue中的元素。
此类也是Java Collections Framework的成员,在 JDK1.5 时由Doug Lea提供。
Q:你提到的 Delayed 元素是什么?
可以看出,该队列中的元素必须实现Delayed接口。
- 一种混合风格的接口,用来标记那些应该在给定的延迟之后执行的对象。此接口的实现必须定义compareTo方法,以提供和getDelay协作的排序。
Q:讲讲它是如何添加元素的吧?
> 毫无疑问,阻塞队列家族必知必会操作。
无论是
add
亦或是
put
- 注意,由于该队列无界,所以 put 方法永远不会阻塞。
都是通过 offer 方法实现的。
offer
- 注意该方法无超时参数,由于无界,所以该队列的元素添加操作都不会阻塞。
- 首先就是加全局唯一的锁,所以该队列是线程安全的。
- 注意到 q,就是组合复用的一个优先队列。
所以其实调用的是优先队列中实现的 offer 入队方法,上一篇中已经描述,不再赘述offer细节。
之后判断 q.peek()获取的队首元素,是否为刚刚放进去的元素,是则唤醒正在等待的一个消费者线程
最后解锁
Q:你刚才offer方法提到的 leader 有什么作用?
指定等待队首元素的线程,即当前获取到锁的消费者线程。
Leader-Follower模式的这种变体可减少不必要的定时等待。当一个线程成为leader时,它仅等待下一个延迟到期,而其他消费者线程依旧将无限期等待。
- 如果leader非空,说明已有消费线程在取数据,直接设置当前线程等待,避免其它消费线程都会无限循环,直到返回第一个元素,这显然很浪费系统资源。
- 如果为空,说明没有其他线程在取数据,设置leader为当前线程,并等待delay到期,直到poll后结束循环
leader线程必须在从take或poll返回前向其他消费者线程发出信号,除非其他消费者线程成为过渡期间的leader。
每当队首被更换为具有更早到期时间的元素,leader属性都会置null失效,并且一些等待线程(不一定是当前leader)被通知信号。 因此,等待线程必须准备好在等待时获得并失去leader能力。
- 等待线程的队列。 当更新的元素在队首变得可用或新线程可能需要成为lead
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
<p> “挨踢”业行情日益严峻,企业招聘门槛愈来愈高,大厂hc更是少之又少,而Java技术面试普遍对基础知识的掌握考察特别深,大多数同学突击所看的 Java 面试基础知识点根本达不到面试官近乎挑剔的要求。 本专刊针对如今的校招及社招痛点,深入解析 JDK 的核心源码,探究 JDK 的设计精髓及最佳实践,同时以模拟面试的场景切入,让同学们在阅读过程中也能轻松掌握面试技巧。 本专刊购买后即可解锁所有章节,故不可以退换哦~ </p>