DelayQueue核心源码解析,Java面试必学 (2)

当更新的元素在队首变得可用或新的线程可能需要成为 leader 时,会发出条件信号

DelayQueue核心源码解析,Java面试必学

图片标题

3 构造方法 3.1 无参

创建一个新的 DelayQueue,它初始是空的

DelayQueue核心源码解析,Java面试必学

图片标题

3.2 有参

创建一个DelayQueue,初始包含Delayed实例的给定集合的元素。

DelayQueue核心源码解析,Java面试必学

图片标题

4 新增数据

先看看继承自 BlockingQueue 的方法

put

将指定的元素插入此延迟队列。 由于队列无界,因此此方法将永远不会阻塞.

DelayQueue核心源码解析,Java面试必学

图片标题可以看到 put 调用的是 offer

DelayQueue#offer

将指定的元素插入此延迟队列

DelayQueue核心源码解析,Java面试必学

图片标题

执行流程

1.加锁 2.元素添加到优先级队列中 3.检验元素是否为队首,是则设置 leader 为null, 并唤醒一个消费线程 4.解锁

其内部调用的是 PriorityQueue 的 offer 方法

PriorityQueue#offer

将指定的元素插入此优先级队列.

public boolean offer(E e) { // 若元素为 null,抛NPE if (e == null) throw new NullPointerException(); // 修改计数器加一 modCount++; int i = size; // 如果队列大小 > 容量 if (i >= queue.length) // => 扩容 grow(i + 1); size = i + 1; // 若队列空,则当前元素正好处于队首 if (i == 0) queue[0] = e; else // 若队列非空,根据优先级排序 siftUp(i, e); return true; } 复制代码 执行流程

元素判空

队列扩容判断

根据元素的 compareTo 方法进行排序,希望最终排序的结果是从小到大的,因为想让队首的都是过期的数据,需要在 compareTo 方法实现.

5 取数据 take

检索并删除此队列的头,如有必要,请等待直到延迟过期的元素在此队列上可用

public E take() throws InterruptedException { final ReentrantLock lock = this.lock; // 获取可中断锁 lock.lockInterruptibly(); try { for (;;) { // 从优先级队列中获取队首 E first = q.peek(); if (first == null) // 队首为 null,说明无元素,当前线程加入等待队列,并阻塞 available.await(); else { // 获取延迟时间 long delay = first.getDelay(NANOSECONDS); if (delay <= 0) // 已到期,获取并删除头部元素 return q.poll(); first = null; // 在等待时不要保留引用 if (leader != null) available.await(); else { Thread thisThread = Thread.currentThread(); leader = thisThread; try { // 线程节点进入等待队列 available.awaitNanos(delay); } finally { if (leader == thisThread) leader = null; } } } } } finally { // 若leader == null且还存在元素,则唤醒一个消费线程 if (leader == null && q.peek() != null) available.signal(); // 解锁 lock.unlock(); } } 复制代码 执行流程

加锁

取出优先级队列的队首

若队列为空,阻塞

若队首非空,获得这个元素的delay时间值,如果first的延迟delay时间值为0的话,说明该元素已经到了可以使用的时间,调用poll方法弹出该元素,跳出方法

若first的延迟delay时间值非0,释放元素first的引用,避免内存泄露

循环以上操作,直至return

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zypdff.html