实战小知识----02.12----延时阻塞队列

90 阅读2分钟

这是我参与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直至获取到第一个数据,其他线程则陷入阻塞等待;

image.png

  • 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线程变量,如果不为空,证明存在别的线程在操作,阻塞等待;如果为空,则赋值当前线程,并且阻塞等待到第一个元素过期为止

image.png