【高并发】深度解析ScheduledThreadPoolExecutor类的源代码( 二 )


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方法的实现 。
overflowFree方法overflowFree方法的源代码如下所示 。
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 + headDelaydelay = Long.MAX_VALUE + headDelay; } //返回延迟时间 return delay;}通过对overflowFree方法的源码分析,可以看出overflowFree方法本质上就是为了限制队列中的所有节点的延迟时间在Long.MAX_VALUE值之内 , 防止在ScheduledFutureTask类中的compareTo方法中溢出 。
ScheduledFutureTask类中的compareTo方法的源码如下所示 。
public int compareTo(Delayed other) { if (other == this) // compare zero if same objectreturn 0; if (other instanceof ScheduledFutureTask) {ScheduledFutureTask<?> x = (ScheduledFutureTask<?>)other;long diff = time - x.time;if (diff < 0)return -1;else if (diff > 0)return 1;else if (sequenceNumber < x.sequenceNumber)return -1;elsereturn 1; } long diff = getDelay(NANOSECONDS) - other.getDelay(NANOSECONDS); return (diff < 0) ? -1 : (diff > 0) ? 1 : 0;}

推荐阅读