源码角度了解阻塞队列之DelayQueue
DelayQueue是延迟队列,在延迟到期的时候才能被获取到,按照到期时间从小到大以二叉堆的形式排列,时间最短的在堆顶的位置,它的成员遍历包括一个Condition条件,一个PriorityQueue队列,一个锁,只允许一个线程生产或消费,还有一个记录leader的线程对象。
put()方法
它的put()方法同样也是直接调用了offer()方法,我们看一下offer():
public boolean offer(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
q.offer(e);
if (q.peek() == e) {
leader = null;
available.signal();
}
return true;
} finally {
lock.unlock();
}
}
- 加锁
- 调用PriorityQueue的offer()方法取元素
- 判断放入的元素如果在堆顶,leader设置为空,唤醒其他线程来消费
- 返回true
- 解锁
take()方法
take()方法的整体功能是检索并取出头部的元素,如果有需要的话等待将要到期的元素,下面我们看一下它的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
if (leader != null)
available.await();
else {
Thread thisThread = Thread.currentThread();
leader = thisThread;
try {
available.awaitNanos(delay);
} finally {
if (leader == thisThread)
leader = null;
}
}
}
}
} finally {
if (leader == null && q.peek() != null)
available.signal();
lock.unlock();
}
}
- 加锁
- for循环,取出堆顶的元素,也就是到期时间最接近的元素
- 判断如果第一个元素为空,显然队列为空,就阻塞等待
- 否则获取第一个元素的延迟时间,如果这个元素的延迟时间小于等于0,说明已经到期,就可以从队列中取出来了,调用q.poll()进行出栈
- 如果延迟时间大于0,说明还没有到期,然后判断leader是否为空,如果不为空的话表示其他线程也在等待这个元素,就一直等待
- 如果leader为空,就把当前线程设置给leader变量,同时设置等待时间为延迟队列元素的到期时间,最后到期后如果leader等于当前线程,再设置为空
- 最后如果当前线程是leader并且判断还有元素,唤醒其他线程来取元素
- 解锁
总结
这篇文章主要介绍了DelayQueue的put()方法和take()方法,它的成员变量有PriorityQueue,放入元素和获取元素都是调用PriorityQueue实例的方法,它主要按照延迟时间来控制什么时候将元素出队
❤️ 感谢大家
如果你觉得这篇内容对你挺有有帮助的话:
- 欢迎关注我❤️,点赞👍🏻,评论🤤,转发🙏
- 关注
盼盼小课堂
,定期为你推送好文,还有群聊不定期抽奖活动,可以畅所欲言,与大神们一起交流,一起学习。