概述
DelayQueue
是java.util.concurrent
中提供的一个很有意思的类。本文将会对DelayQueue
做一个介绍,然后列举应用场景。并且提供一个Delayed
接口的实现和 Sample 代码。
DelayQueue
是一个无界的BlockingQueue
,用于放置实现了Delayed
接口的对象,其中的对象只能在其到期时才能从队列中取走。这种队列是有序的,即队头对象的延迟到期时间最长。注意:不能将null元素放置到这种队列中。
Delayed,一种混合风格的接口,用来标记那些应该在给定延迟时间之后执行的对象。Delayed扩展了Comparable
接口,比较的基准为延时的时间值,Delayed
接口的实现类getDelay的返回值应为固定值(final
)。DelayQueue
内部是使用PriorityQueue
实现的。
应用举例
DelayQueue
阻塞队列在我们系统开发中也常常会用到,例如:缓存系统的设计,缓存中的对象,超过了空闲时间,需要从缓存中移出;任务调度系统,能够准确的把握任务的执行时间。我们可能需要通过线程处理很多时间上要求很严格的数据,如果使用普通的线程,我们就需要遍历所有的对象,一个一个的检查看数据是否过期等,首先这样在执行上的效率不会太高,其次就是这种设计的风格也大大的影响了数据的精度。一个需要12:00点执行的任务可能12:01才执行,这样对数据要求很高的系统有更大的弊端。由此我们可以使用DelayQueue
。
我们在网咖或者网吧上网时会用到一个网吧综合系统,其中有一个主要功能就是给每一位网民计时,用户充值一定金额会有相应的上网时常,这里我们用DelayQueue模拟实现一下:
用DelayQueue
存储网民(Wangmin
类),每一个考生都有自己的名字和完成试卷的时间,Wangba
线程对DelayQueue
进行监控,从队列中取出到时间的网民执行下机操作。
实现了Delayed
接口的网民类
public class Wangmin implements Delayed {
private String name;
//身份证
private String id;
//截止时间
private long endTime;
//定义时间工具类
private TimeUnit timeUnit = TimeUnit.SECONDS;
public Wangmin(String name,String id,long endTime){
this.name=name;
this.id=id;
this.endTime = endTime;
}
public String getName(){
return this.name;
}
public String getId(){
return this.id;
}
/**
* 用来判断是否到了截止时间
*/
@Override
public long getDelay(TimeUnit unit) {
//return unit.convert(endTime, TimeUnit.MILLISECONDS) - unit.convert(System.currentTimeMillis(), TimeUnit.MILLISECONDS);
return endTime - System.currentTimeMillis();
}
/**
* 相互批较排序用
*/
@Override
public int compareTo(Delayed delayed) {
Wangmin w = (Wangmin)delayed;
return this.getDelay(this.timeUnit) - w.getDelay(this.timeUnit) > 0 ? 1:0;
}
}
网吧类
public class WangBa implements Runnable {
private DelayQueue<Wangmin> queue = new DelayQueue<Wangmin>();
public boolean yingye =true;
/**
* 上机
*/
public void shangji(String name,String id,int money){
Wangmin man = new Wangmin(name, id, 1000 * money + System.currentTimeMillis());
System.out.println("网名"+man.getName()+" 身份证"+man.getId()+"交钱"+money+"块,开始上机...");
this.queue.add(man);
}
// 下机
public void xiaji(Wangmin man){
System.out.println("网名"+man.getName()+" 身份证"+man.getId()+"时间到下机...");
}
@Override
public void run() {
while(yingye){
try {
Wangmin man = queue.take();
xiaji(man);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String args[]){
try{
System.out.println("网吧开始营业");
WangBa siyu = new WangBa();
Thread shangwang = new Thread(siyu);
shangwang.start();
siyu.shangji("路人甲", "123", 1);
siyu.shangji("路人乙", "234", 10);
siyu.shangji("路人丙", "345", 5);
}
catch(Exception e){
e.printStackTrace();
}
}
}
源码分析
首先看一下DlayedQueue
源码
public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
implements BlockingQueue<E> {
private final transient ReentrantLock lock = new ReentrantLock();
private final PriorityQueue<E> q = new PriorityQueue<E>();
// ...
}
这是一小部分源码,从这段源码可以看出,DelayedQueue中处理的是实现Delayed接口的任务,DelayedQueue使用lock来实现线程同步,使用PriorityQueue来管理任务。那么Delayed接口是什么呢?
public interface Delayed extends Comparable<Delayed> {
long getDelay(TimeUnit unit);
}
这是Delayed
接口的官方注释,意思是:一个混合风格的接口,为创建给定延迟的任务。(翻译的不太好,请纠正);其中有两个重要的方法compareTo和getDelay,第一个是比较两个任务的延迟时间进行排序,第二个方法用来获取延迟时间。
priorityQueue
是一种优先级队列,这里优先级就是延迟时间,也就是说进入队列的任务安装优先级进行排序,延迟时间最短的在队列前面,先被处理,也就是说,每次从队列中取出的任务都将是到期的任务。比如我们实现一个缓存,当某个key-value对超期了,我们就可以从队列前取出,然后进行销毁操作。
这里有一个实现缓存的例子,链接如下:
http://www.cnblogs.com/jobs/archive/2007/04/27/730255.html