高并发编程-队列-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(); } }

浙公网安备 33010602011771号