前面介绍了优先级队列PriorityBlockingQueue,顺带也说了一下PriorityQueue,两者的实现方式是一模一样的,都是采用基于数组的平衡二叉堆实现,不论入队的顺序怎么样,take、poll出队的节点都是按优先级排序的。但是PriorityBlockingQueue/PriorityQueue队列中的所有元素并不是在入队之后就已经全部按优先级排好序了,而是只保证head节点即队列的首个元素是当前最小或者说最高优先级的,其它节点的顺序并不保证是按优先级排序的,PriorityBlockingQueue/PriorityQueue队列只会在通过take、poll取走head之后才会再次决出新的最小或者说最高优先级的节点作为新的head,其它节点的顺序依然不保证。所以通过peek拿到的head节点就是当前队列中最高优先级的节点。
明白了优先级队列的原理要理解DelayQueue就非常简单,因为DelayQueue就是基于PriorityQueue实现的,DelayQueue队列实际上就是将队列元素保存到内部的一个PriorityQueue实例中的(所以也不支持插入null值),DelayQueue只专注于实现队列元素的延时出队。
延迟队列DelayQueue是一个无界阻塞队列,它的队列元素只能在该元素的延迟已经结束或者说过期才能被出队。它怎么判断一个元素的延迟是否结束呢,原来DelayQueue队列元素必须是实现了Delayed接口的实例,该接口有一个getDelay方法需要实现,延迟队列就是通过实时的调用元素的该方法来判断当前元素是否延迟已经结束。
既然DelayQueue是基于优先级队列来实现的,那肯定元素也要实现Comparable接口,没错因为Delayed接口继承了Comparable接口,所以实现Delayed的队列元素也必须要实现Comparable的compareTo方法。延迟队列就是以时间作为比较基准的优先级队列,这个时间即延迟时间,这个时间大都在构造元素的时候就已经设置好,随着程序的运行时间的推移,队列元素的延迟时间逐步到期,DelayQueue就能够基于延迟时间运用优先级队列并配合getDelay方法达到延迟队列中的元素在延迟结束时精准出队。
Delayed接口
1 public interface Delayed extends Comparable<Delayed> { 2 3 //以指定的时间单位unit返回此对象的剩余延迟 4 // 返回值 小于等于0 表示延迟已经结束 5 long getDelay(TimeUnit unit); 6 }
放入DelayQueue队列的元素必须实现Delayed接口,getDelay方法用于查看当前对象的延迟剩余时间,返回值是以参数指定的unit为单位的数字(unit可以是秒,分钟等),返回值小于等于0就表示该元素延迟结束。注意:该接口的实现类必须要实现compareTo方法,且compareTo方法提供与getDelay方法一致的排序,也就是说compareTo要基于getDelay方法的返回值来实现比较。
DelayQueue
现来看看它的成员变量:
1 public class DelayQueue<E extends Delayed> extends AbstractQueue<E> 2 implements BlockingQueue<E> { 3 4 //非公平锁 5 private final transient ReentrantLock lock = new ReentrantLock(); 6 private final PriorityQueue<E> q = new PriorityQueue<E>(); //优先级队列 7 8 /* Thread designated to wait for the element at the head of the queue. This variant of the Leader-Follower pattern (http://www.cs.wustl.edu/~schmidt/POSA/POSA2/) serves 9 * to minimize unnecessary timed waiting. When a thread becomes the leader, it waits only for the next delay to elapse, but other threads await indefinitely. 10 * The leader thread must signal some other thread before returning from take() or poll(...), unless some other thread becomes leader in the interim. 11 * Whenever the head of the queue is replaced with an element with an earlier expiration time, the leader field is invalidated by being reset to null, 12 * and some waiting thread, but not necessarily the current leader, is signalled. So waiting threads must be prepared to acquire and lose leadership while waiting. 13 14 * 指定用于等待队列头部的元素的线程。这种Leader-Follower模式的变体(http://www.cs.wustl.edu/~schmidt/POSA/POSA2/)可以减少不必要的定时等待。 15 * 当一个线程成为leader时,它只等待下一个延迟过期,而其他线程则无限期地等待。leader线程必须在从take()或poll(…)返回之前向其他线程发出信号,除非其他线程在此期间成为leader。 16 * 每当队列的头部被具有更早过期时间的元素替换时,leader字段就会通过重置为null而无效,并且会通知等待的线程(不一定是当前的leader)的信号。 17 * 因此,等待线程必须准备好在等待时获得和失去领导权。 18 */ 19 private Thread leader = null; 20 21 /* Condition signalled when a newer element becomes available at the head of the queue or a new thread may need to become leader. 22 * 当队列头部的新元素可用或新线程可能需要成为leader时发出的条件。 23 */ 24 private final Condition available = lock.newCondition();