补充部分---ScheduledThreadPoolExecutor类分析 线程池底层原理详解与源码分析( 二 )

代码说明
1.三个标注的参数是任务中主要的成员变量 。
2.其次,我们会发现callable的任务是没有间隔周期的:因为callable本身就是阻塞等待,而且周期性的也不合适 。
3.实现了RunnableScheduledFuture接口,其主要方法isPeriodic()用于判断是不是周期任务,又继承了RunnableFuture接口.
4.ScheduledFutureTask又继承了FutureTask类,而FutureTask类实现了RunnableFuture接口 。(故感觉RunnableFuture接口的那些方法挺重要的)
5.RunnableFuture接口主要是由Runnable和Future两大接口组成(自己去看继承关系),主要有run()方法 。
2.ScheduledFutureTask类#run方法
代码展示
// 重写FutureTask,如果是周期性任务需要重新放入队列public void run() {// 检查当前状态 不能执行任务,则取消任务if (!canRunInCurrentRunState(this))cancel(false);//如果不是周期任务,调用FutureTask.run()执行任务(非周期任务直接执行)else if (!isPeriodic())super.run();// 周期性任务else if (super.runAndReset()) {//与run方法的不同就是正常完成后任务的状态不会变化,依旧是NEW,且返回值为成功或失败,不会设置result属性setNextRunTime(); //设置任务下次执行时间reExecutePeriodic(outerTask);}}代码说明
1.这里面很明显存在一个隐患,那就是没有捕捉异常,所以如果我们自定义的run()方法中如果没有捕捉异常的话,那么出现异常的时候我们容易两眼摸瞎 。
2.故使用定时任务的时候,自定义的run方法需要自行捕捉异常进行处理 。
3.ScheduledFutureTask类#setNextRunTime方法
代码展示
//判断指定的任务是否为定期任务private void setNextRunTime() {long p = period; //取出周期时间if (p > 0)time += p; //time是周期任务的下一次执行时间elsetime = triggerTime(-p);}// ScheduledThreadPoolExecutor中的方法long triggerTime(long delay) {//delay 的值是否小于 Long.MAX_VALUE 的一半,是的话,当前时间+延迟时间return System.nanoTime() + ((delay < (Long.MAX_VALUE >> 1)) ? delay : overflowFree(delay));}// ScheduledThreadPoolExecutor中的方法private long overflowFree(long delay) {//获取队列中的首节点Delayed head = (Delayed) super.getQueue().peek();//获取的节点不为空,则进行后续处理if (head != null) {//从队列节点中获取延迟时间long headDelay = head.getDelay(NANOSECONDS);//如果从队列中获取的延迟时间小于0,并且传递的delay值减去从队列节点中获取延迟时间小于0if (headDelay < 0 && (delay - headDelay < 0))//将delay的值设置为Long.MAX_VALUE + headDelay(该数字为负数)delay = Long.MAX_VALUE + headDelay;}//返回延迟时间return delay;}代码说明
1.周期时间period有正有负,这是ScheduledThreadPoolExecutor的ScheduledAtFixedRate和ScheduledWithFixedDelay的方法区别,前者为正数,后者为负数 。2.正数时,下一次执行时间为原来的执行时间+周期,即以执行开始时间为基准 。3.负数时,不考虑溢出情况,下一次执行时间为当前时间+周期,即以执行结束时间为基准 。如果溢出,下一次执行时间为Long.MAX_VALUE + headDelay 。
疑问说明(这一步有兴趣的需要自己去调试然后在核心方法处断点查看就可以了)
其实只要当做作System.nanoTime() + delay就可以了,没必要关注overflowFree这一步,原因:
1.如果执行了  Long.MAX_VALUE + headDelay,triggerTime方法会获得负数,示例代码
executor.scheduleAtFixedRate(task, 20, 1244574199069500L, TimeUnit.NANOSECONDS);//任延迟取最大值 稳定定时器executor.scheduleWithFixedDelay(task, 1, 9223272036854775807L, TimeUnit.NANOSECONDS); //任务+延迟2.如果不执行  Long.MAX_VALUE + headDelay,triggerTime方法也有可能获得负数,示例代码:
executor.scheduleAtFixedRate(task, 20, 4611686018427387900L, TimeUnit.NANOSECONDS);executor.scheduleWithFixedDelay(task, 1, 9223272036854775807L, TimeUnit.NANOSECONDS);

经验总结扩展阅读