前言
最近有朋友提出希望可以介绍一下介绍下常见的定时任务的实现方案,于是便有了这篇文章。
Timer
本次主要讨论大家使用较多的方案,首先第一个就是 Timer 定时器,它可以在指定时间后运行或周期性运行任务;使用方法也非常简单:
![08f8f90b09c3e0b470f1101be6f6bf34.png](https://img-blog.csdnimg.cn/img_convert/08f8f90b09c3e0b470f1101be6f6bf34.png)
这样便可创建两个简单的定时任务,分别在 3s/5s 之后运行。
使用起来确实很简单,但也有不少毛病,想要搞清楚它所存在的问题首先就要理解其实现原理。
实现原理
定时任务要想做到按照我们给定的时间进行调度,那就得需要一个可以排序的容器来存放这些任务。
在 Timer 中内置了一个 TaskQueue 队列,用于存放所有的定时任务。
![bfb1b43432f7962d91f7cabaa628899d.png](https://img-blog.csdnimg.cn/img_convert/bfb1b43432f7962d91f7cabaa628899d.png)
其实本质上是用数组来实现的一个最小堆,它可以让每次写入的定时任务都按照执行时间进行排序,保证在堆顶的任务执行时间是最小的。
这样在需要执行任务时,每次只需要取出堆顶的任务运行即可,所以它取出任务的效率很高为
![d01b7d8b1d452b857e87354187be8740.png](https://img-blog.csdnimg.cn/img_convert/d01b7d8b1d452b857e87354187be8740.png)
。
结合代码会比较容易理解:
![62a719a5264e7fedc4287fbbfccb0122.png](https://img-blog.csdnimg.cn/img_convert/62a719a5264e7fedc4287fbbfccb0122.png)
在写入任务的时候会将一些基本属性存放起来(任务的调度时间、周期、初始化任务状态等),最后就是要将任务写入这个内置队列中。
![fdc32ff97e4019e08c2ae878e072efec.png](https://img-blog.csdnimg.cn/img_convert/fdc32ff97e4019e08c2ae878e072efec.png)
![e9ff95366f39e4f784ebca610f23f3fb.png](https://img-blog.csdnimg.cn/img_convert/e9ff95366f39e4f784ebca610f23f3fb.png)
在任务写入过程中最核心的方法便是这个 fixUp() ,它会将写入的任务从队列的中部通过执行时间与前一个任务做比对,一直不断的向前比较。
如果这个时间是最早执行的,那最后将会被移动到堆顶。