DalayQueue 是一个支持延时获取元素的无界阻塞队列,队列使用PriorityQueue来实现,队列中的元素必须实现Delayed接口
运用场景:
-
缓存系统的设计,可以保存缓存元素的有效期,一旦能从DelayQueue中获取元素,表示缓存有效期到了
-
定时任务调度,使用DelayQueue保存当天将会执行的任务和执行时间,一旦从DelayQueue中获取到任务就开始执行
一个例子:
public class DelayQueueExample {
static class DelayTask implements Delayed{
private Date date;
DelayTask(Date date){
this.date = date;
}
/**
* 根据时间单位获取该任务被执行的剩余时间,如果取出是负数,将会被DelayQueue取出
* @param unit
* @return
*/
@Override
public long getDelay(TimeUnit unit) {
long now = date.getTime() - new Date().getTime();
return unit.convert(now,TimeUnit.MILLISECONDS);
}
/**
* DelayQueue中的 priorityQueue将会根据这个方法对元素进行排序
* @param o
* @return
*/
@Override
public int compareTo(Delayed o) {
if(this == o)
return 0;
long d = (getDelay(TimeUnit.NANOSECONDS) -
o.getDelay(TimeUnit.NANOSECONDS));
return (d == 0) ? 0 : ((d < 0) ? -1 : 1);
}
}
public static void main(String[] args) throws InterruptedException {
DelayQueue<DelayTask> delayQueue = new DelayQueue();
for(int i=10;i>0;i--){
Date date = new Date(new Date().getTime()+i*1000);
DelayTask delayTask = new DelayTask(date);
delayQueue.put(delayTask);
}
while(!delayQueue.isEmpty()){
System.out.println(delayQueue.take().date);
}
}
}
输出
Thu Sep 13 17:13:43 CST 2018
Thu Sep 13 17:13:44 CST 2018
Thu Sep 13 17:13:45 CST 2018
Thu Sep 13 17:13:46 CST 2018
Thu Sep 13 17:13:47 CST 2018
Thu Sep 13 17:13:48 CST 2018
Thu Sep 13 17:13:49 CST 2018
Thu Sep 13 17:13:50 CST 2018
Thu Sep 13 17:13:51 CST 2018
Thu Sep 13 17:13:52 CST 2018
来看下实现延时阻塞的代码
/**
* Retrieves and removes the head of this queue, waiting if necessary
* until an element with an expired delay is available on this queue.
*
* @return the head of this queue
* @throws InterruptedException {@inheritDoc}
*/
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); //获取还需要多久执行, 单位纳秒
if (delay <= 0)
return q.poll(); //如果时间到了,取出数据
first = null; // don't retain ref while waiting
if (leader != null) //如果发现不为空,说明前面有线程已经在等待取数据了,那么这里直接进入等待
available.await();
else {
Thread thisThread = Thread.currentThread();
leader = thisThread; //将自己的线程赋予leader,这样后来的线程都会在上一步阻塞
try {
available.awaitNanos(delay); //将自己挂起到可以执行任务,再经历一次循环 判断是否可以执行,防止意外唤醒
} finally {
if (leader == thisThread)
leader = null;
}
}
}
}
} finally {
if (leader == null && q.peek() != null)
available.signal(); //唤醒其他线程来取数据
lock.unlock();
}
}
/**
* Inserts the specified element into this delay queue.
*
* @param e the element to add
* @return {@code true}
* @throws NullPointerException if the specified element is null
*/
public boolean offer(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
q.offer(e); //这里的q是priorityQueue,根据Delayed接口的compareTo来进行优先级排序
if (q.peek() == e) {
leader = null; //如果没有线程来取数据,则唤醒一个
available.signal();
}
return true;
} finally {
lock.unlock();
}
}