• DelayQueue—-一种有序队列,特点就是只有在队列中的元素到期后才能取出。

    1.内存中哪些对象到了超时时间,需要从内存中清除出去。
    2.服务器连接中有哪些连接很长时间未操作,需要关闭这些连接
    3.任务中有哪些到了执行时间,该进行调度了。

public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
    implements BlockingQueue<E> {

    private transient final ReentrantLock lock = new ReentrantLock();
    private final PriorityQueue<E> q = new PriorityQueue<E>();  堆实现的
    private Thread leader = null;
    private final Condition available = lock.newCondition();
    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(TimeUnit.NANOSECONDS);
                    if (delay <= 0)
                        return q.poll();
                    else if (leader != null)
                        available.await();
                    else {
                        Thread thisThread = Thread.currentThread();
                        leader = thisThread;
                        try {
                            available.awaitNanos(delay);
                        } finally {
                            if (leader == thisThread)
                                leader = null;
                        }
                    }
                }
            }
        } finally {
            if (leader == null && q.peek() != null)
                available.signal();
            lock.unlock();
        }
    }

简单的方法就是写一个线程不断去检查每一项是否到了时间,但这种方法在队列中的元素非常多的情况下会不准确或太耗费性能,用DelayQueue可完美地解决这个问题。

马上就要过年了,每次回老家都要悲催地去镇上人挤人的网吧上网。下面就拿这个做个例子:

假设镇上开了一家无限大的网吧,每人交一块钱可以上一分钟网(过年时的网吧坑爹程度差不多就这样);
网吧比较老式没有先进的管理系统,为了知道哪个家伙时间到该下机了,老板得一台一台地去看,上网的人一多等老板把整个网吧转一遍有些家伙早超时了,而且老板要一遍一遍地检查也累个半死;
有一天某个人给老板开发了个简单的程序,这个程序可以自动将上机的人排序,并在恰当的时间告诉老板哪个家伙时间到了。这个程序大概会是这样:

public class Wangming implements Delayed {

    private String name;
    //身份证
    private String id;
    //截止时间
    private long endTime;

    public Wangming(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) {
        // TODO Auto-generated method stub
        return endTime-System.currentTimeMillis();
    }

    /**
     * 相互批较排序用
     */
    @Override
    public int compareTo(Delayed o) {
        // TODO Auto-generated method stub
        Wangming jia = (Wangming)o;
        return endTime-jia.endTime>0?1:0;
    }

}
public class WangBa implements Runnable {

    private DelayQueue<Wangming> queue = new DelayQueue<Wangming>();
    public boolean yinye =true;

    public void shangji(String name,String id,int money){
        Wangming man = new Wangming(name,id,1000*60*money+System.currentTimeMillis());      
        System.out.println("网名"+man.getName()+" 身份证"+man.getId()+"交钱"+money+"块,开始上机...");
        this.queue.add(man);
    }

    public void xiaji(Wangming man){
        System.out.println("网名"+man.getName()+" 身份证"+man.getId()+"时间到下机...");
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        while(yinye){
            try {
                System.out.println("检查ing");
                Wangming man = queue.take();
                xiaji(man);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                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", 2);
            siyu.shangji("路人丙", "345", 1);
        }
        catch(Exception ex){

        }

    }
}

网吧开始营业
检查ing
网名路人甲 身份证123交钱1块,开始上机…
网名路人乙 身份证234交钱2块,开始上机…
网名路人丙 身份证345交钱1块,开始上机…
网名路人甲 身份证123时间到下机…
检查ing
网名路人丙 身份证345时间到下机…
检查ing
网名路人乙 身份证234时间到下机…
检查ing

从上面的运行结果可以看出,程序并没有一遍一遍地扫描队列中的对象,而是在队列中没有对象符合条件时阻塞。
至于这个好不好用呢,用过才知道,尤其是在并发量比较大的情况下。

DelayQueue 的内部是一个优先队列,从数据结构上看是一个类有序二叉树:

offer、poll、remove() 和 add—— O(log(n)) 时间;
remove(Object) 和 contains(Object) ——线性时间;
peek、element 和 size——-固定时间

DelayQueue是一个BlockingQueue,其特化的参数是Delayed。(不了解BlockingQueue的同学,先去了解BlockingQueue再看本文)
Delayed扩展了Comparable接口,比较的基准为延时的时间值,Delayed接口的实现类getDelay的返回值应为固定值(final)。DelayQueue内部是使用PriorityQueue实现的。
DelayQueue = BlockingQueue + PriorityQueue + Delayed
DelayQueue的关键元素BlockingQueue、PriorityQueue、Delayed。可以这么说,DelayQueue是一个使用优先队列(PriorityQueue)实现的BlockingQueue,优先队列的比较基准值是时间。

public interface Comparable<T> {
    public int compareTo(T o);
}
public interface Delayed extends Comparable<Delayed> {
    long getDelay(TimeUnit unit);
}
public class DelayQueue<E extends Delayed> implements BlockingQueue<E> { 
    private final PriorityQueue<E> q = new PriorityQueue<E>();
}