package com58.zhl.concurrent; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Random; import java.util.concurrent.DelayQueue; import java.util.concurrent.Delayed; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; /** * 基于DelayQueue实现一个超时管理 * @author 58_zhl * */ public class Cpt8_TimeoutManager { private static List<String> result=new ArrayList<String>(); /** * 遍历队列以便观察结果 * @param queue */ private static String iteratorDelayQueue(DelayQueue<Session> queue){ Iterator<Session> it=queue.iterator(); StringBuilder sb=new StringBuilder(); while(it.hasNext()){ Session dt=it.next(); sb.append(dt.toString()+"\n"); } return sb.toString(); } public static void testDelayQueue(){ DelayQueue<Session> queue=new DelayQueue<Session>(); Random random=new Random(47); StringBuilder sb=new StringBuilder(); List<Session> list=new ArrayList<Session>(); //生产对象添加到队列中 for(int i=0;i<5;i++){ long timeout=(random.nextInt(10)+1)*1000; //11以内的整数乘以1000毫秒 Session temp=new Session(timeout); sb.append("id:"+temp.id+"-").append(timeout).append(" "); list.add(temp); queue.offer(temp); } System.out.println("=========================添加到队列中的顺序========================="); System.out.println(sb.toString()); //可以先观察queue的排序结果 System.out.println("=========================队列中实际的顺序========================"); System.out.println(iteratorDelayQueue(queue)); System.out.println("=========================启动清理线程=============================="); monitorThread(queue); //启动监控清理线程 //可先不执行延迟清理,进行观察 updateObject(list,queue); //模拟因session最新被调用,而延迟清理 } public static void monitorThread(final DelayQueue<Session> queue){ Thread thread=new Thread(){ public void run(){ while(!queue.isEmpty()){ try { Session dt=queue.take(); String str=dt.toString2()+"=====已被清理"+"\t currentTime:"+System.currentTimeMillis(); System.out.println(str); result.add(str); result.add(iteratorDelayQueue(queue)); //每次清理之后,都要保存一下队列的快照 } catch (InterruptedException e) { System.out.println("清理中断...."); return; } } System.out.println("清理完所有缓存!!!!!"); System.out.println("======================延迟对象生命周期运行时queue的快照========================"); for(String tstr:result){ System.out.println(tstr); } System.out.println("=============================================="); } }; thread.start(); } /** * 模拟在清理session池的时候,session因重新调用而导致清理时间延迟 * @param list */ public static void updateObject(final List<Session> list,final DelayQueue<Session> queue){ Thread thread=new Thread(){ public void run(){ try { //对于iteratorDelayQueue可能存在同步的问题,但是这里因sleep时间点的问题,不会发生异常 //暂时不需要处理 Thread.sleep(1000); //睡眠1000ms //list(4)默认生命周期是2000ms,睡眠1000后,现在应该还有1000ms左右 list.get(4).updateTriger(); result.add("id:"+list.get(4).id+" 寿命延长\t currentTime:"+System.currentTimeMillis()); Thread.sleep(1000); //再次睡眠1000ms //再次延长list(4),这次延时后list(4)的总生命周期应该是4000ms list.get(4).updateTriger(); result.add("id:"+list.get(4).id+" 寿命延长\t currentTime:"+System.currentTimeMillis()); //执行到此处时,一共睡眠了2000ms,list(1)的初始生命是6000ms,此时延迟应该总共生命周期为8000ms list.get(1).updateTriger(); result.add("id:"+list.get(2).id+" 寿命延长\t currentTime:"+System.currentTimeMillis()); } catch (InterruptedException e) { } } }; thread.start(); } /** * 超时对象封装 * @author 58_zhl * */ static class Session implements Delayed{ /** * 用于生成ID */ private static AtomicInteger objectId=new AtomicInteger(0); /** * 本对象的ID */ private int id=objectId.incrementAndGet(); //注意与getAndIncrement方法的区别 /** * 到期时间 */ private AtomicLong triger=null; /** * 创建时间 */ private long createTime=System.nanoTime(); /** * 该对象被更新的次数 */ private AtomicInteger count=new AtomicInteger(0); /** * 该对象的超时时间,对象存在的生命周期 */ private final long timeout; public Session(long timeout){ this.timeout=TimeUnit.NANOSECONDS.convert(timeout, TimeUnit.MILLISECONDS); //保存对象 this.triger=new AtomicLong(createTime+this.timeout); //对象的到期时间 } /** * 更新超时时间,每使用某个对象时,应该调用此方法, * 来延长对象在内存的生命周期 */ public void updateTriger(){ /* * 统计被更新的次数,在对象创建后,因再次使用导致对象的生命周期延长的次数 */ count.getAndIncrement(); long oldValue=0; //获取当前值 long newValue=0; do{ oldValue=triger.get(); newValue=System.nanoTime()+timeout; }while(!triger.weakCompareAndSet(oldValue, newValue)); } /** * 对比方法,按照从大到小的顺序 * 在offer的时候只调用compareTo方法 */ @Override public int compareTo(Delayed o) { Session dt=(Session)o; System.out.println("compareTo()方法被调用....id:"+id+"\t currentTime:"+System.currentTimeMillis()); return (this.triger.get()>dt.triger.get()?1:(this.triger.get()<dt.triger.get()?-1:0)); } /** * 返回与次对象相关的剩余延迟时间,注意,该方法和compareTo必须保持一致的排序 * 离当前时间越远的时间,返回的值越大,这样与compareTo方法的排序结果保持一致 * * 在take的时候仅调用getDelay方法,但是compareTo方法将影响到新添加元素在队列 * 中的位置,而这个位置可能决定getDelay方法的检索 */ @Override public long getDelay(TimeUnit unit) { //unit是时间单位,如果triger中保存的不是unit单位的时间,则可以进行转换 //注意:这里一定要进行单位转换,只有这样才能达到最优的阻塞状态,而不是进行不停的循环 long remainTime=triger.get()-System.nanoTime(); System.out.println("id:"+id+"\tDelay:"+remainTime+" ns\tTimeUnit:"+unit.name()); return remainTime; } public String toString() { long start=createTime/1000000; long end=triger.get() / 1000000; return "id:" + id + "\t创建时间:" + start + " ms\t到期时间:" + end + " ms\t生命周期:" + (timeout / 1000000) + " ms"; } public String toString2(){ long start=createTime/1000000; long end=triger.get() / 1000000; return "id:"+id+"总寿命:"+ (end-start)+" ms被更新的次数:"+ count.get(); } } public static void main(String args[]){ /** * DelayQueue内置封装了PriorityQueue。在进入take操作时,仅仅是在内置PriorityQueue对象上执行poll操作。 * 实际上在DelayQueue中,是不会调用compareTo方法的,但是在每次offer元素到DelayQueue中时,首先将元素 * 压入内置的PriorityQueue,然后判断当前压入到PriorityQueue的元素是否比压入当前元素之前的队列头元素 * 还要小,如果还要小,则立刻执行available.signalAll();来唤醒DelayQueue的take操作。详情请参见代码实现 * DelayQueue.take、DelayQueue.offer方法 * * PriorityQueue实现了弹出队列头部后的重排序操作,因PriorityQueue的重排序较复杂,因此我们仅需要知道每当 * poll操作后,PriorityQueue都会对优先级队列进行重排序就可以了。但是如果队列头元素一直未能poll,则不会 * 进行重排序。简单来说:PriorityQueue中的元素,一但成为头元素,那么它将不会再次成为中间节点。 * 另外,PriorityQueue在offer的时候,也会插入到合适的位置并重排序,及使队列处于按优先级排序状态。 */ testDelayQueue(); //测试超时管理队列 } /** * ====以下为运行结果简要说明 * * compareTo()方法被调用....id:2 currentTime:1387271463047 * compareTo()方法被调用....id:2 currentTime:1387271463047 * compareTo()方法被调用....id:3 currentTime:1387271463047 * compareTo()方法被调用....id:3 currentTime:1387271463047 * compareTo()方法被调用....id:4 currentTime:1387271463047 * compareTo()方法被调用....id:4 currentTime:1387271463047 * compareTo()方法被调用....id:4 currentTime:1387271463048 * compareTo()方法被调用....id:5 currentTime:1387271463048 * compareTo()方法被调用....id:5 currentTime:1387271463048 * compareTo()方法被调用....id:5 currentTime:1387271463048 * =========================添加到队列中的顺序========================= * id:1-9000 id:2-6000 id:3-4000 id:4-2000 id:5-2000 * =========================队列中实际的顺序======================== * id:4 创建时间:95280919 ms 到期时间:95282919 ms 生命周期:2000 ms * id:5 创建时间:95280919 ms 到期时间:95282919 ms 生命周期:2000 ms * id:2 创建时间:95280918 ms 到期时间:95286918 ms 生命周期:6000 ms * id:1 创建时间:95280917 ms 到期时间:95289917 ms 生命周期:9000 ms * id:3 创建时间:95280918 ms 到期时间:95284918 ms 生命周期:4000 ms * * =========================启动清理线程============================== * id:4 Delay:1998754758 ns TimeUnit:NANOSECONDS // TODO 第一次排序后,启动线程发现需要堵塞1998754758纳秒 * id:4 Delay:-383338 ns TimeUnit:NANOSECONDS // TODO 当堵塞指定时间后立刻调用getDelay方法,发现返回值小于0,立刻弹出 * compareTo()方法被调用....id:5 currentTime:1387271465049 //弹出过程中,还会进行重排序 * compareTo()方法被调用....id:3 currentTime:1387271465049 * compareTo()方法被调用....id:3 currentTime:1387271465049 * id:4总寿命:2000 ms被更新的次数:0=====已被清理 currentTime:1387271465049 * id:5 Delay:1000503689 ns TimeUnit:NANOSECONDS * id:5 Delay:998916441 ns TimeUnit:NANOSECONDS * id:5 Delay:-1077481 ns TimeUnit:NANOSECONDS * compareTo()方法被调用....id:3 currentTime:1387271467051 * compareTo()方法被调用....id:1 currentTime:1387271467051 * id:5总寿命:4001 ms被更新的次数:2=====已被清理 currentTime:1387271467051 * id:3 Delay:-2868151 ns TimeUnit:NANOSECONDS * compareTo()方法被调用....id:2 currentTime:1387271467051 * id:3总寿命:4000 ms被更新的次数:0=====已被清理 currentTime:1387271467051 * id:2 Delay:3998453678 ns TimeUnit:NANOSECONDS * id:2 Delay:-194911 ns TimeUnit:NANOSECONDS * id:2总寿命:8002 ms被更新的次数:1=====已被清理 currentTime:1387271471050 * id:1 Delay:996749726 ns TimeUnit:NANOSECONDS * id:1 Delay:-1034123 ns TimeUnit:NANOSECONDS * id:1总寿命:9000 ms被更新的次数:0=====已被清理 currentTime:1387271472048 * 清理完所有缓存!!!!! * ======================延迟对象生命周期运行时queue的快照======================== * id:5 寿命延长 currentTime:1387271464049 //TODO 寿命虽然延长了,但是不能立刻生效,因为只有等到第一个take元素弹出后,才有机会进行重排序 * id:4总寿命:2000 ms被更新的次数:0=====已被清理 currentTime:1387271465049 //TODO 第一个元素弹出了 * id:5 创建时间:95280919 ms 到期时间:95283920 ms 生命周期:2000 ms //TODO 这是弹出后,并且重排序之后的结果 * id:3 创建时间:95280918 ms 到期时间:95284918 ms 生命周期:4000 ms * id:2 创建时间:95280918 ms 到期时间:95286918 ms 生命周期:6000 ms * id:1 创建时间:95280917 ms 到期时间:95289917 ms 生命周期:9000 ms * * id:5 寿命延长 currentTime:1387271465050 * id:3 寿命延长 currentTime:1387271465050 * id:5总寿命:4001 ms被更新的次数:2=====已被清理 currentTime:1387271467051 * id:3 创建时间:95280918 ms 到期时间:95284918 ms 生命周期:4000 ms * id:1 创建时间:95280917 ms 到期时间:95289917 ms 生命周期:9000 ms * id:2 创建时间:95280918 ms 到期时间:95288920 ms 生命周期:6000 ms * * id:3总寿命:4000 ms被更新的次数:0=====已被清理 currentTime:1387271467051 * id:2 创建时间:95280918 ms 到期时间:95288920 ms 生命周期:6000 ms * id:1 创建时间:95280917 ms 到期时间:95289917 ms 生命周期:9000 ms * * id:2总寿命:8002 ms被更新的次数:1=====已被清理 currentTime:1387271471050 * id:1 创建时间:95280917 ms 到期时间:95289917 ms 生命周期:9000 ms * * id:1总寿命:9000 ms被更新的次数:0=====已被清理 currentTime:1387271472048 * * ============================================== */ }