通过源码可以看出,scheduleAtFixedRate方法将传递的Runnable对象封装成ScheduledFutureTask任务对象,并设置了执行周期,下一次的执行时间相对于上一次的执行时间来说,加上了period时长,时长的具体单位由TimeUnit决定 。采用固定的频率来执行定时任务 。
ScheduledThreadPoolExecutor类中另一个定时调度任务的方法是scheduleWithFixedDelay方法,接下来,我们就一起看看scheduleWithFixedDelay方法 。
scheduleWithFixedDelay方法scheduleWithFixedDelay方法的源代码如下所示 。
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) { //传入的Runnable对象和TimeUnit为空,则抛出空指针异常 if (command == null || unit == null)throw new NullPointerException(); //任务延时时长小于或者等于0,则抛出非法参数异常 if (delay <= 0)throw new IllegalArgumentException(); //将Runnable对象封装成ScheduledFutureTask任务 //并设置固定的执行周期来执行任务 ScheduledFutureTask<Void> sft =new ScheduledFutureTask<Void>(command, null,triggerTime(initialDelay, unit), unit.toNanos(-delay)); //调用decorateTask方法,本质上直接返回ScheduledFutureTask任务 RunnableScheduledFuture<Void> t = decorateTask(command, sft); //设置执行的任务 sft.outerTask = t; //执行延时任务 delayedExecute(t); //返回任务 return t;}
从scheduleWithFixedDelay方法的源代码,我们可以看出在将Runnable对象封装成ScheduledFutureTask时,设置了执行周期,但是此时设置的执行周期与scheduleAtFixedRate方法设置的执行周期不同 。此时设置的执行周期规则为:下一次任务执行的时间是上一次任务完成的时间加上delay时长,时长单位由TimeUnit决定 。也就是说,具体的执行时间不是固定的,但是执行的周期是固定的,整体采用的是相对固定的延迟来执行定时任务 。
如果大家细心的话,会发现在scheduleWithFixedDelay方法中设置执行周期时,传递的delay值为负数,如下所示 。
ScheduledFutureTask<Void> sft =new ScheduledFutureTask<Void>(command, null, triggerTime(initialDelay, unit), unit.toNanos(-delay));
这里的负数表示的是相对固定的延迟 。
在ScheduledFutureTask类中,存在一个setNextRunTime方法,这个方法会在run方法执行完任务后调用,这个方法更能体现scheduleAtFixedRate方法和scheduleWithFixedDelay方法的不同,setNextRunTime方法的源码如下所示 。
private void setNextRunTime() { //距离下次执行任务的时长 long p = period; //固定频率执行,//上次执行任务的时间 //加上任务的执行周期 if (p > 0)time += p; //相对固定的延迟 //使用的是系统当前时间 //加上任务的执行周期 elsetime = triggerTime(-p);}
在setNextRunTime方法中通过对下次执行任务的时长进行判断来确定是固定频率执行还是相对固定的延迟 。
triggerTime方法在ScheduledThreadPoolExecutor类中提供了两个triggerTime方法,用于获取下一次执行任务的具体时间 。triggerTime方法的源码如下所示 。
private long triggerTime(long delay, TimeUnit unit) { return triggerTime(unit.toNanos((delay < 0) ? 0 : delay));}long triggerTime(long delay) { return now() +((delay < (Long.MAX_VALUE >> 1)) ? delay : overflowFree(delay));}
这两个triggerTime方法的代码比较简单,就是获取下一次执行任务的具体时间 。有一点需要注意的是:delay < (Long.MAX_VALUE >> 1判断delay的值是否小于Long.MAX_VALUE的一半,如果小于Long.MAX_VALUE值的一半,则直接返回delay,否则需要处理溢出的情况 。
我们看到在triggerTime方法中处理防止溢出的逻辑使用了overflowFree方法,接下来,我们就看看overflowFree方法的实现 。
经验总结扩展阅读
- 不满情绪大爆发如何安抚
- 5个小妙招提高BB食欲
- 2022年12月结婚黄道吉日婚嫁吉利日子一览
- 千万别学电子信息工程 有什么原因
- 2023就业前景好的10大专业 哪些专业工资高
- 2023物联网技术主要学什么课程 毕业能做哪些工作
- 新高考什么专业冷门 有哪些冷门专业
- 2023专科最吃香的十大专业 什么专业热门工资高
- 新高考选专业推荐 什么专业好
- 2023年订婚最好的日子有哪些 2023年订婚最好的日子一览