高并发编程-队列-BlockingQueue-DelayQueue

高并发编程-队列-BlockingQueue-DelayQueue

一、DelayQueue简介

          DelayQueue 支持延时的阻塞队列,内部采用采用优先队列PriorityQueue,同时实现了Delayed 接口,当创建元素的时候可以指定用户多久才能从队列中获取当前元素,延时满的才能出队。延时队列DelayQueue的特点:它是按照先结束延时的顺序出队。它是一个无界的队列,放入的元素必须实现Delayed,Delayed又继承了Comparable,代码如下:

public interface Delayed extends Comparable<Delayed> {

    /**
     * getDelay 方法返回的是“还剩下多长的延迟时间才会被执行”,
     * 如果返回 0 或者负数则代表任务已过期。
     *元素会根据延迟时间的长短被放到队列的不同位置,越靠近队列头代表越早过期。
     */
    long getDelay(TimeUnit unit);
}

二、特点

特点:一个使用优先级队列的无界阻塞队列
数据结构:priorityQueue与priorityBlockingQueue,没有阻塞功能
锁:ReenTrantLock
阻塞对象:Condition available
入队:不阻塞,因为是无界队列
出队:检查元素的过期时间,如果时间小于等于0,则出队。大于0说明没有过期,继续阻塞,判断leader线程是否阻塞,如果阻塞,说明已有线程阻塞,直接阻塞。如果为空,把当前线程变成leader线程,阻塞
应用场景: 1.订单超时关闭  2.异步短信通知功能 3.任务超时处理 等

三、DelayQueue的使用

     

DelayQueue delayQueue=new DelayQueue();

四、DelayQueue的原理

//设置锁
private final transient ReentrantLock lock = new ReentrantLock();
//设置优先队列
private final PriorityQueue<E> q = new PriorityQueue<E>();
//标记当前排队的线程,leader指向第一个从队列获取元素的阻塞线程
private Thread leader = null;
//条件队列,唤醒通知leader
private final Condition available = lock.newCondition();
//创建一个初始为空的延时队列
public DelayQueue() {}
//创建一个初始的延时队列,包含Collection数据
public DelayQueue(Collection<? extends E> c) {
        this.addAll(c);
    }

五、入队

//放入队列中 
public void put(E e) {
        offer(e);
    }
   //入队方法
  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方法

  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);
                    //小于等于0说明到期,出队
                    if (delay <= 0)
                        return q.poll();
                  //first GC
                  first = null; 
                   //如果大于0,说明延时未到继续阻塞
                    if (leader != null)
                     //  leader不为空,继续阻塞
                        available.await();
                    else {
                        Thread thisThread = Thread.currentThread();
                        //把当前线程给leader
                        leader = thisThread;
                        try {
                   //让条件队列等待delay时间后唤醒
                   //醒来后重新进入循环判断,看是否到期
                  //唤醒过来也不一定可以拿到元素,可能被其他线程拿走
                   //条件锁的唤醒分成两步,先从Condition的队列里出队
                   // 再入队到AQS的队列中,当其它线程调用LockSupport.unpark(t)的时候才会真正唤醒
                            available.awaitNanos(delay);
                        } finally {
                       //清空leader,让其他元素也有机会获取数据
                            if (leader == thisThread)
                                leader = null;
                        }
                    }
                }
            }
        } finally {
   //如果堆顶还有元素,唤醒下一个线程
            if (leader == null && q.peek() != null)
                available.signal();
            lock.unlock();
        }
    }    

 

 

 

     

 

posted @ 2021-12-10 18:25  小亲年  阅读(192)  评论(0)    收藏  举报