ScheduledThreadPoolExecutor是一个可实现定时任务的线程池,ScheduledThreadPoolExecutor内的任务既可以在设定的时间到达时执行一次,也可以相隔固定时间周期执行。
ScheduledThreadPoolExecutor继承自ThreadPoolExecutor,关于ThreadPoolExecutor的原理可参考:
ScheduledThreadPoolExecutor相比较于ThreadPoolExecutor,只多了四个提交任务的方法,这四个方法实现自ScheduledExecutorService,继承自ThreadPoolExecutor的execute和submit方法都调用了自己的schedule方法
- 第一个schedule的任务是runnable,不支持获取返回值,会在指定延迟时间之后执行一次
- 第二个schedule的任务是callable,可获取返回值,会在指定延迟时间之后执行一次
- scheduleAtFixedRate的任务为runnable,不支持获取返回值,会在指定延迟时间后执行一次,之后按设定好的间隔时间重复执行
- scheduleWithFixedDelay的任务为runnable,不支持获取返回值,会在指定延迟时间后执行一次,并在执行完成时间的基础上按间隔时间周期执行
后两个方法可能理解起来不是很清楚,简单点说,scheduleAtFixedRate的下一次任务的执行时间为:法定的任务开始时间(并不是任务真正开始执行的时间,因为线程池是在任务执行完成后再计算下一次的开始时间,此时计算出的开始时间可能已经小于现在的时间了,当这个任务执行的时候,真实的执行时间肯定比设定好的时间大)+间隔;scheduleWithFixedDelay的下一次任务执行时间为:任务结束时间+间隔。
我们将scheduleAtFixedRate演示一下,正常情况如下,其中0、5、15、20就是法定的任务开始时间
不正常的情况如下
实现原理
ScheduledThreadPoolExecutor之所以能够实现上述的定时功能,本质上是通过两点实现的:
1.将任务包装在ScheduledFutureTask内。ScheduledFutureTask相当于原始任务的代理,在执行原始任务之后会视情况将任务的执行时间修改后再加入工作队列
2.使用了DelayedWorkQueue作为任务队列。DelayedWorkQueue内部是一个ScheduledFutureTask的数组,queue的功能类似于DelayQueue,在设定的时间到达时才能取出队列中的元素。
下面我们基于以上两点来分析源码。
1.构造方法
以最后一个构造函数为例,四个构造方法都调用了父类(ThreadPoolExecutor)的构造方法,最大线程数都设置为int最大值,线程存活时间都为0,工作队列都为DelayedWorkQueue
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
//最大线程数为int最大值,线程空转时间为0,使用DelayedWorkQueue作为工作队列
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue(), threadFactory, handler);
}
2.任务提交
普通的schedule方法会将间隔时间设为