专注Java教育14年 全国咨询/投诉热线:400-8080-105
动力节点LOGO图
始于2009,口口相传的Java黄埔军校
首页 学习攻略 Java学习 Java基础学习:Java timer定时器调度器实现原理

Java基础学习:Java timer定时器调度器实现原理

更新时间:2020-04-02 14:57:31 来源:动力节点 浏览1991次


  Timer 简单易用,其源码阅读起来也非常清晰,本节我们来仔细分析一下 Timer 类,来看看 JDK 源码的编写者是如何实现一个稳定可靠的简单调度器。


  Timer 使用


  Timer 调度任务有一次性调度和循环调度,循环调度有分为固定速率调度(fixRate)和固定时延调度(fixDelay)。固定速率就好比你今天加班到很晚,但是到了第二天还必须准点到公司上班,如果你一不小心加班到了第二天早上 9 点,你就连休息的时间都没有了。而固定时延的意思是你必须睡够 8 个小时再过来上班,如果你加班到凌晨 6 点,那就可以下午过来上班了。固定速率强调准点,固定时延强调间隔。


Java基础学习:Java timer定时器调度器实现原理


  如果你有一个任务必须每天准点调度,那就应该使用固定速率调度,并且要确保每个任务执行时间不要太长,千万别超过了第二天这个点。如果你有一个任务需要每隔几分钟跑一次,那就使用固定时延调度,它不是很在乎你的单个任务要跑多长时间。


  内部结构


  Timer 类里包含一个任务队列和一个异步轮训线程。任务队列里容纳了所有待执行的任务,所有的任务将会在这一个异步线程里执行,切记任务的执行代码不可以抛出异常,否则会导致 Timer 线程挂掉,所有的任务都没得执行了。单个任务也不易执行时间太长,否则会影响任务调度在时间上的精准性。比如你一个任务跑了太久,其它等着调度的任务就一直处于饥饿状态得不到调度。所有任务的执行都是这单一的 TimerThread 线程。


Java基础学习:Java timer定时器调度器实现原理


  堆排序


  Timer 的任务队列 TaskQueue 是一个特殊的队列,它内部是一个数组。这个数组会按照待执行时间进行堆排序,堆顶元素总是待执行时间最小的任务。轮训线程会每次轮训出时间点最近的并且到点的任务来执行。数组会自动扩容,如果任务非常多。


Java基础学习:Java timer定时器调度器实现原理


  任意线程都可以通过 Timer.schedule 方法将任务加入 TaskQueue,但是 TaskQueue 又并不是线程安全的数据结构。所在每次修改 TaskQueue 时都需要加锁。


Java基础学习:Java timer定时器调度器实现原理


  任务状态


  TimerTask 有 4 个状态,VIRGIN 是默认状态,刚刚实例化还没有被调度。SCHEDULED 表示已经将任务塞进 TaskQueue 等待被执行。EXECUTED 表示任务已经执行完成。CANCELLED 表示任务被取消了,还没来得及执行就被人为取消了。

  

Java基础学习:Java timer定时器调度器实现原理


  对于一个循环任务来说,它不存在 EXECUTED 状态,因为它每次刚刚执行完成,就被重新调度了。EXECUTED 状态仅仅存在于一次性任务,而且这个状态其实并不是表示任务已经执行完成,它是指已经从任务队列里摘出来了,马上就要执行。

  任务间隔字段 period 比较特殊,当使用固定速率时,period 为正值,当使用固定间隔时,period 为负值,当任务是一次性时,period 为零。下面是循环任务的下次调度时间设定

  

Java基础学习:Java timer定时器调度器实现原理


  对于固定速率来说,如果任务执行时间太长超出了间隔,那么它可能会持续霸占任务队列,因为它的调度时间将总是低于 currentTime,排在堆顶,每次轮训取出来的都是它。运行完毕后,重新调度这个任务,它的时间依旧赶不上。持续下去你会看到这个任务的调度时间远远落后于当前时间,而其它任务可能会彻底饿死。这就是为什么一定要特别注意固定速率的循环任务运行时间不宜过长。


  任务锁


Java基础学习:Java timer定时器调度器实现原理


  Timer 的任务支持取消操作,取消任务的线程和执行任务的线程极有可能不是一个线程。有可能任务正在执行中,结果另一个线程表示要取消任务。这时候 Timer 是如何处理的呢?在 TimerTask 类里看到了一把锁。当任务属性需要修改的时候,都会加锁。

  

Java基础学习:Java timer定时器调度器实现原理


  在任务运行之前会检查任务是不是已经被取消了,如果取消了,就从队列中移除。一旦任务开始运行 run(),对于单次任务来说它就无法被取消了,而循环任务将不会继续下次调度。如果任务没有机会得到执行(时间设置的太长),那么即使这个任务被取消了,它也会一直持续躺在任务队列中。设想如果你调度了一系列久远的任务,然后都取消了,这可能会成为一个内存泄露点。所以 Timer 还单独提供了一个 purge() 方法可以一次性清空所有的已取消的任务。

  

Java基础学习:Java timer定时器调度器实现原理

  以上就是动力节点java培训机构的小编针对“Java基础学习:Java timer定时器调度器实现原理”的内容进行的回答,希望对大家有所帮助,如有疑问,请在线咨询,有专业老师随时为你服务。


提交申请后,顾问老师会电话与您沟通安排学习

免费课程推荐 >>
技术文档推荐 >>