结论

其实自己在看这个源码的时候,只是看到这两个方法都是周期性执行任务的,但是没有仔细去看两个方法的细节,所以,这篇笔记主要记录两者的区别
整个源码细节看下来之后,我认为这两个方法最大的一个区别是:
scheduleAtFixedRate是在上一次任务的开始时间的基础之上,加上指定的时间间隔,作为当前任务执行的开始时间
scheduleWithFixedDelay是在上一次任务执行完毕之后的基础之上,加上指定的时间间隔,作为当前任务执行的开始时间


我们假如定时执行周期都设置为5S,任务执行需要耗时2S,以这个窗口作为执行时间刻度的话,区别就是上面图中所画的这样

应用

这个代码的意思是:初始化了一个ScheduledThreadPoolExecutor,然后执行执行间隔是5S,每个任务中休眠了2S,模拟需要耗时2S的操作

scheduleAtFixedRate

public class ScheduledThreadPoolTest {public static void main(String[] args) {ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(4);SimpleDateFormat sm = new SimpleDateFormat("hh:MM:ss");System.out.println("当前时间是:"+sm.format(new Date()));scheduledThreadPoolExecutor.scheduleAtFixedRate(new Runnable() {@Overridepublic void run() {try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("这是执行的内容:" + sm.format(new Date()));}}, 5, 5, TimeUnit.SECONDS);}
}

打印结果:

当前时间是:08:03:57
这是执行的内容:08:03:04
这是执行的内容:08:03:09
这是执行的内容:08:03:14
这是执行的内容:08:03:19

scheduleWithFixedDelay

public class ScheduledThreadPoolTest {public static void main(String[] args) {ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(4);SimpleDateFormat sm = new SimpleDateFormat("hh:MM:ss");System.out.println("当前时间是:"+sm.format(new Date()));scheduledThreadPoolExecutor.scheduleWithFixedDelay(new Runnable() {@Overridepublic void run() {try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("这是执行的内容:" + sm.format(new Date()));}}, 5, 5, TimeUnit.SECONDS);}
}

执行结果:

当前时间是:08:03:06
这是执行的内容:08:03:13
这是执行的内容:08:03:20
这是执行的内容:08:03:27
这是执行的内容:08:03:34

根据以上两个结果可以看到,在我们指定了执行周期都是5S的情况下,scheduleAtFixedRate是以第一次任务开启的时间作为起始值 + 5S,不管这个任务执行耗时是多久,都是5S开启一次
scheduleWithFixedDelay是以第一次任务结束的时间作为起始值 + 5S,作为下一次任务开始的时间

源码

public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit) {if (command == null || unit == null)throw new NullPointerException();if (period <= 0)throw new IllegalArgumentException();ScheduledFutureTask<Void> sft =new ScheduledFutureTask<Void>(command,null,triggerTime(initialDelay, unit),unit.toNanos(period));RunnableScheduledFuture<Void> t = decorateTask(command, sft);sft.outerTask = t;delayedExecute(t);return t;}
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit) {if (command == null || unit == null)throw new NullPointerException();if (delay <= 0)throw new IllegalArgumentException();ScheduledFutureTask<Void> sft =new ScheduledFutureTask<Void>(command,null,triggerTime(initialDelay, unit),unit.toNanos(-delay));RunnableScheduledFuture<Void> t = decorateTask(command, sft);/*** 对于需要重复执行的任务,会在这里将任务赋值到一个变量中*/sft.outerTask = t;delayedExecute(t);return t;}

对于上面两个方法的源码,其实我们可以发现,逻辑基本是一样的,只是有一个区别:
scheduleWithFixedDelay在new ScheduledFutureTask的时候,将delay参数设置为了负数,这个区别点,就是为什么fixedDelay是以上一次任务结束时间 + 周期间隔作为下次任务开始时间的原因

1.首先是判断任务或者unit是否为null,如果true,就抛出空指针异常
2.如果执行间隔周期小于0,也抛出异常
3.然后根据当前任务和执行间隔周期值生成对应的scheduledFutureTask对象;这里其实就是对要执行的任务进行了一层封装,添加了一些额外的参数
4.将包装好的任务保存在一个全局变量中
5.delayedExecute()是将任务添加到线程池的队列中,需要注意的是:这里会添加到scheduledThreadPoolExecutor的内部队列 delayWorkQueue中

对于这么一个方法,要做的事情就完成了,那我们需要考虑下,是如何做到定时执行的?其实原理很简单,在执行任务的时候,如果需要重复执行,就再次把任务入队即可
入队之后优先级的判断,是delayWordQueue自己内部的逻辑,
那我入队了一个任务之后,是谁去取出这个任务执行的呢?
这个问题我之前一直在迷茫,结果后来才想到,scheduledThreadPoolExecutor本身就是一个线程池啊,那当然是threadPoolExecutor中的逻辑去依次取出队列中的任务去执行,只是在调用出队方法的时候,会调用delayWordQueue的出队方法
这样看起来,底层源码的设计就清晰明了的了,各个组件或者各个模块负责自己的事情,如果你要对我所负责的事情进行扩展,没关系,只要按照我的要求来就可以了

对于delayWorKQueue出队、入队之后优先级的处理就不说了,我们接着来说如何重复执行

前面有说过,对于程序员在调用了scheduleAtFixedRate()方法之后,会把要执行的任务包装成ScheduledFutureTask对象,所以,线程池在执行任务的时候,调用的也是ScheduledFutureTask的run()方法

/*** Overrides FutureTask version so as to reset/requeue if periodic.* 自定义的任务在执行的时候,实际调用的就是这个方法,因为线程池对任务进行了一层包装*/
public void run() {/*** 1.首先判断是否需要重复执行,这个值是在初始化的时候,指定的* 如果只需要执行一次,这里返回的就是false* 如果需要周期定时执行,这里返回的就是true* 根据period的值来判断*/boolean periodic = isPeriodic();/*** 2.这里没看懂,判断是否需要取消任务?*/if (!canRunInCurrentRunState(periodic))cancel(false);/*** 3.如果只需要执行一次,就会执行这里的逻辑*/else if (!periodic)ScheduledFutureTask.super.run();/*** 4.如果是需要重复执行的,就执行这里的方法* 如果任务正常执行成功,就继续设置下次的执行时间* setNextRunTime():是在当前时间的基础之上,加上第一次指定的延迟时间* reExecutePeriodic():是将任务再次加入队列中*/else if (ScheduledFutureTask.super.runAndReset()) {setNextRunTime();reExecutePeriodic(outerTask);}
}

isPeriodic()方法就是判断period是否为0
return period != 0;

那period是在什么时候赋值的呢?在new ScheduledFutureTask()的时候,这里就和上面我说 scheduleWithFixedDelay()方法,把周期执行间隔设置为负数对应上了
也就是说如果是周期执行的方法,period都不为0

runAndReset()方法是去调用程序员指定的要执行的代码块,也就是所谓的任务,执行成功之后,会接着调用两个方法

setNextRunTime()方法是设置下次任务执行时间
reExecutePeriodic()其实就是把任务再次入队

/*** Sets the next time to run for a periodic task.* 设置下次运行时间,* 如果是fixedRate,就是在当前time的基础上 + 初始化时指定的时间间隔* 如果是fixedDelay,就是在当前时间now()的基础上 + 指定的时间间隔*/
private void setNextRunTime() {long p = period;if (p > 0)time += p;elsetime = triggerTime(-p);
}

首先需要知道:
scheduleAtFixedRate方法的period是大于0的
scheduleWithFixedDelay的period是小于0的

这里可以看到,对于time(任务开始执行时间)的设置,对于p > 0的场景,是在上次任务开始时间的基础上,加上设置的执行间隔
对于p < 0的场景,是调用了triggerTime(-p);方法


/*** Returns the trigger time of a delayed action.* 在fixedDelay方法被调用的时候,如果是设置下一次执行时间,就是now() + delay*/
long triggerTime(long delay) {return now() +((delay < (Long.MAX_VALUE >> 1)) ? delay : overflowFree(delay));
}

这个判断中,就是now() + delay 因为delay一般设置的都是小于 Long.MAX_VALUE >> 1的

所以我们也可以推断出:
scheduleAtFixedRate方法是在上一次任务执行的开始时间的基础上 + 任务周期执行间隔作为下次任务开始时间
scheduleWithFixedDelay方法是在上一次任务执行完毕的时间基础上 + 任务周期执行间隔作为下次任务开始时间

ScheduledThreadPoolExecutor之scheduleWithFixedDelay和scheduleAtFixedRate的区别相关推荐

  1. scheduleWithFixedDelay和scheduleAtFixedRate的区别

    scheduleWithFixedDelay使用 public class MainDemo {public static int times = 0;public static void main( ...

  2. 拒绝字面意思的忽悠:scheduleWithFixedDelay与scheduleAtFixedRate的真正区别

    字面意思,一个固定间隔,一个固定频率. 但具体有啥区别,很多文章都没有讲清楚. 我专门用demo跑了一下 scheduleWithFixedDelay 执行周期=任务执行时长+间隔时长 schedul ...

  3. schedule() 和 scheduleAtFixedRate() 的区别--转载

    1.  schedule() ,2个参数方法: 在执行任务时,如果指定的计划执行时间scheduledExecutionTime <= systemCurrentTime,则task会被立即执行 ...

  4. 面试|详细分析ScheduledThreadPoolExecutor(周期性线程池)的原理

    ScheduledThreadPoolExecutor 在进一步了解ScheduledThreadPoolExecutor类之前,先学习下ScheduledFutureTask类的构造. 1. Sch ...

  5. scheduleAtFixedRate与schedule区别

    schedule和scheduleAtFixedRate的区别: 如果指定开始执行的时间在当前系统运行时间之前,scheduleAtFixedRate会把已经过去的时间也作为周期执行(追赶性)而下一次 ...

  6. ScheduledThreadPoolExecutor详解

    2019独角兽企业重金招聘Python工程师标准>>> 本文主要分为两个部分,第一部分首先会对ScheduledThreadPoolExecutor进行简单的介绍,并且会介绍其主要A ...

  7. scheduledthreadpoolexecutor使用_ScheduledThreadPoolExecutor详解

    本文主要分为两个部分,第一部分首先会对ScheduledThreadPoolExecutor进行简单的介绍,并且会介绍其主要API的使用方式,然后介绍了其使用时的注意点,第二部分则主要对Schedul ...

  8. Java并发编程—ScheduledThreadPoolExecutor原理分析

    原文作者:小付 原文地址:ScheduledThreadPoolExecutor原理分析 目录 一.简单使用 二.类UML图 三.处理流程 四.任务提交方式 五.SchduledFutureTask之 ...

  9. android startanimation 回调,ScheduledThreadPoolExecutor执行莫名停止问题Android几个动画回调运行线程...

    本文记录两个问题: ScheduleThreadPoolExecutor莫名停止执行.Animation和Animator两个动画回调监听 运行在哪个线程. 一:ScheduleThreadPoolE ...

  10. Java定时任务之--schedule与scheduleAtFixedRate异同

    相同点: 任务执行未超时,下次执行时间 = 上次执行开始时间 + period:任务执行超时,下次执行时间 = 上次执行结束时间:在任务执行未超时时,它们都是上次执行时间加上间隔时间,来执行下一次任务 ...

最新文章

  1. 【Big Data】HADOOP集群的配置(一)
  2. python使用matplotlib可视化3D线框图、线框图可以将数据投影到指定的三维表面上,并输出可视化程度较高的三维效果图
  3. 基于.htaccess的Web Shell工具htshells
  4. 高并发编程-Daemon Thread的创建以及使用场景分析
  5. SharePoint:扩展DVWP - 第21部分:实现可维护的三级联动下拉框
  6. 我们距离“云”还有多远?
  7. OpenCV isContinuous()连续存储的问题
  8. Linux环境编译动态库和静态库总结
  9. Java中json格式的字符串数组,list,json,map相互转换
  10. vue中v-for的使用以及注意事项
  11. 集群故障处理之处理思路以及健康状态检查(三十二)
  12. 六级考研单词之路-三十二
  13. foxpro获取html数据类型,FoxPro数据库写入html文件中
  14. 股票证券交易系统架构分析与设计
  15. 注意力机制Effective Approaches to Attention-based Neural Machine Translation
  16. 一场云端的“神仙打架”:BAT加华为的影响未来之争
  17. 五子棋游戏图形化实现
  18. C语言指针(一)——什么是指针及指针的定义
  19. 让dedecms栏目页标题显示页码数
  20. SpringCloud微服务搭建(四 搭建EurekaServer集群)

热门文章

  1. C/C++[PAT B1009]说反话
  2. 自动驾驶7-4 自动驾驶汽车简介全面总结 Congratulations on Completing Course 1
  3. linux虚拟内存和win,linux下的vm(虚拟内存)和windows下的作用是一样的,均是防止真实内存资源不足准备的. linux的vm相关参数...
  4. 轻量级神经网络ShuffleNet
  5. 《统计学习方法》小结
  6. DataType error: cannot resolve DataType of [[[D
  7. Git来回切换版本的时候,pom文件变黄,每次都需要重新添加到maven以及修改后文件不生效的解决方法
  8. C++/QT控制通过VISA控制硬件设备,超级容易学会的控制硬件方法
  9. java.util.function包下的四大Function
  10. python 迭代器 生成器 区别_Python的生成器和迭代器之间的区别