DelayQueue是一个无界的BlockingQueue,用于存储实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取走。这种队列是有序的,即队头对象的延迟到期时间最长。注意:不能将null元素放置到这种队列中。
下面先看一个例子:
/**
* DelayQueue需要存储的对象的类,实现Delayed接口,重写compareTo和getDelay方法
*/
class DelayedQueueEvent implements Delayed {
private Long startDate;
private String name;
public DelayedQueueEvent(Long startDate, String name) {
this.startDate = startDate;
this.name = name;
}
public String getName() {
return this.name;
}
@Override
public int compareTo(Delayed o) {
long result = this.getDelay(TimeUnit.NANOSECONDS) - o.getDelay(TimeUnit.NANOSECONDS);
if (result < 0) {
return -1;
} else if (result > 0) {
return 1;
}
return 0;
}
@Override
public long getDelay(TimeUnit unit) {
long diff = startDate - System.currentTimeMillis();
return unit.convert(diff, TimeUnit.MILLISECONDS);
}
}
public class DelayedQueueDemo {
public static void main(String[] args) throws Exception {
DelayQueue<DelayedQueueEvent> queue = new DelayQueue<>();
for (int i = 0; i < 10; i++) {
//startDate启动时间为当前时间延后5秒的整数倍
DelayedQueueEvent event = new DelayedQueueEvent(System.currentTimeMillis() + (i+1) * 5000, String.valueOf(i));
queue.add(event);
}
System.out.println("当前时间:"+ new Date());
DelayedQueueEvent event;
while (queue.size() != 0) {
event = queue.poll();
if (event != null) {
System.out.println("取出" + event.getName() + ":" + new Date());
}
}
}
}
运行结果如下:
由结果可知每5秒从
DelayQueue中取出一个元素。
例子很简单,那么有两个问题:
1.添加的元素为什么需要排序呢?
查看源码可知:
private final PriorityQueue<E> q = new PriorityQueue<E>();
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
E first = q.peek();
if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0)
return null;
else
return q.poll();
} finally {
lock.unlock();
}
}
原因是DelayQueue采用了PriorityQueue来存储数据,而PriorityQueue所存储的元素是有序的。
2.compareTo方法应该返回什么样的排序?
此接口的实现必须定义一个
compareTo方法,该方法提供与此接口的 getDelay方法一致的排序
。
从以上的源码也可以看出,从DelayQueue获取元素时候,是获取PriorityQueue的头元素,并判断是否到达执行的时间,如果没到执行时间,返回null。如果 compareTo提供的排序和getDelay不一致,那么该处就会有问题,就会出现到达执行时间的元素没有被取出。
注意:
getDelay方法返回与此对象相关的剩余延迟时间,以给定的时间单位表示, 查看源码可知DelayQueue给的单位是
纳秒
.