DelayQueue是一个无界阻塞队列,队列中的元素比较特殊,必须是实现了Delayed接口的元素。Delayed接口是一个混合接口,它继承了Comparator接口。它也具有PriorityBlockingQueue的特征,元素中优化级最高的元素是延迟时间最长的元素。队列头的元素是呆在队列时间最长的元素,它只有到时期,才能出队。即getDelay获取到的时间小于等于0时,否则返回null元素。
DelayQueue的并发控制同样使用ReentrantLock和它的Condition对象来实现。因为添加元素不阻塞,所以也只有一个Condition对象来实现等待/通知模式。DelayQueue同样不允许使用null元素。
一、DelayQueue的结构
DelayQueue的结构和PriorityBlockingQueue基本一致,它持有一个PriorityQueue的引用
各种方法实现也委托给了PriorityQueue对象来实现。另外还有一个ReentrantLock和它的Condition对象;队列中的元素是Delayed接口类型的元素。
Delayed接口定义:
1 |
public interface Delayed extends Comparable<Delayed> { |
DelayQueue的主要成员变量:
1 |
private transient final ReentrantLock lock = new ReentrantLock(); |
二、DelayQueue的主要方法实现
1、入队操作
入队操作因为是无界队列,所有不会出现阻塞,put/offer都正常添加到队列中。
1 |
public boolean offer(E e) { |
2、出队操作
因为出队必须是到期的元素,如果获取不到元素,阻塞版本的take会阻塞等待到delay的时间到期,而超时版本的poll会返回null。具体的实现都大同小异,只看take方法的实现:
1 |
public E take() throws InterruptedException { |
具体的实现流程如下:
1).获取锁,并加锁;
2).开始自旋,利用peek方法获取队列的头元素,如果获取失败,则释放锁,进入Condition的等待队列;
3).如果能获取到队列头的元素,则判断到期时间;如果还未到期,则继续在剩下的时间中await;
4).如果元素已经到时,则获取成功,poll出队,同时判断队列如果非空,则继续通知阻塞的线程;
5).最后返回获取到的元素;
三、使用DelayQueue
DelayQueue可以用于很多场景,比如缓存过期管理、会话过期管理、连接超时管理等。下面的例子是使用DelayQueue来管理缓存中过期的元素。
1、保存数据的键值对类:
1 |
public class Pair<K, V> { |
2、实现Delayed接口:
1 |
public class DelayItem<T> implements Delayed { |
3、缓存实现和测试
1 |
public class Cache<K,V> { |