这是我参与2022首次更文挑战的第20天,活动详情查看:2022首次更文挑战
本篇讲述延时阻塞队列 —— DelayQueue
DelayQueue
- DelayQueue是一种特殊的优先队列,它是无界的,内部有一个优先队列。一般用于延时业务,例如外卖订单的30分钟内无支付取消订单;任务超时处理等。
private final PriorityQueue<E> q = new PriorityQueue<E>(); - PriorityQueue是一个无界队列,内部存在构造方法,可以通过实例化时传入Comparator 比较器进行比较排序。
- DelayQueue要求每个元素都继承 Delay 接口,然后Delayed扩展了Comparable接口,因为每个Delay的元素都是可以比较的
public class DelayQueue<E extends Delayed> extends AbstractQueue<E> implements BlockingQueue<E> {
使用
- 存在几个参数
- ReentrantLock 可重入锁,保护所有访问
- PriorityQueue 优先队列
- Thread leader 存放需要操作的第一个等待线程,用于优化阻塞通知
- Condition 显示锁
- leader参数类型为线程的Thread,官方注释是下图这样的;意思就是,减少其他线程的阻塞等待;作为一个通知去实现。假如有多个线程需要取出数据,如果有一个线程取到了,设置leader为自己,并且等待延时delay直至获取到第一个数据,其他线程则陷入阻塞等待;
- add方法,其中调用的是offer方法
public boolean add(E e) { return offer(e); } - offer方法
- 获取锁并锁住当前
- 优先队列插入数据
- 如果插入的数据就是第一条,则设置leader为null并且唤醒所有线程
- 解锁
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();
}
}
- take方法的逻辑——返回第一个过期的元素,如果没有则阻塞等待使用了一个final修饰的私有ReentrantLock锁保护所有元素。弹出第一个过期元素,null则阻塞;其中有一个leader线程变量,如果不为空,证明存在别的线程在操作,阻塞等待;如果为空,则赋值当前线程,并且阻塞等待到第一个元素过期为止