ScheduledThreadPoolExecutor
继承ThreadPoolExecutor
类,实现ScheduledExecutorService
接口。它是一个支持定时执行或者延时执行的线程池。
ScheduledThreadPoolExecutor的使用
ScheduledExecutorService
的定义如下:
1 | public interface ScheduledExecutorService extends ExecutorService { |
两个schedule
方法的区别就在于,前者传入的是Runnable
而后者传入的是Callable
,因此后者可以获得任务执行的结果而前者不可以。
scheduleAtFixedRate()
方法和scheduleWithFixedDelay()
方法看起来十分相似,我们来看看他们之间的区别。
scheduleAtFixedRate
编写一个测试方法:
1 | private static void scheduleAtFixedRate(ScheduledExecutorService service, final int sleepTime) { |
执行时间小于间隔时间
执行scheduleAtFixedRate
方法:
1 | scheduleAtFixedRate(service, 1000); |
执行结果:
1 | scheduleAtFixedRate 开始执行时间:10:47:55 |
可以看到:在任务执行时间小于间隔时间的情况下,程序以起始时间为准则,每隔指定时间执行一次,不受任务执行时间影响。
执行时间大于间隔时间
执行scheduleAtFixedRate
方法:
1 | scheduleAtFixedRate(service, 6000); |
执行结果:
1 | scheduleAtFixedRate 开始执行时间:10:50:20 |
可以看到,在任务执行时间大于间隔时间的情况下,此方法不会重新开启一个新的任务进行执行,而是等待原有任务执行完成,马上开启下一个任务进行执行。此时,执行间隔时间已经被打乱。
scheduleWithFixedRate
编写一个测试方法:
1 | private static void scheduleWithFixedDelay(ScheduledExecutorService service, final int sleepTime) { |
执行时间小于间隔时间
执行scheduleWithFixedDelay
方法:
1 | scheduleWithFixedDelay(service, 1000); |
执行结果:
1 | scheduleWithFixedDelay 开始执行时间:10:54:26 |
可以看到:当执行任务小于延迟时间时,第一个任务执行之后,延迟指定时间,然后开始执行第二个任务。
执行时间大于间隔时间
执行scheduleWithFixexDelay
方法:
1 | scheduleWithFixedDelay(service, 6000); |
执行结果:
1 | scheduleWithFixedDelay 开始执行时间:11:28:18 |
可以看到:当执行任务大于延迟时间时,第一个任务执行之后,延迟指定时间,然后开始执行第二个任务。
无论任务的执行时间长短,scheduleWithFixedDelay
方法都是当第一个任务执行完成之后,延迟指定时间再开始执行第二个任务。
ScheduledThreadPoolExecutor原理
首先我们来查看一下ScheduledThreadPoolExecutor
的新建过程。
前面我们的示例中是这样新建ScheduledExecutorService
的:
ScheduledExecutorService service = Executors.newScheduledThreadPool(10)
Executors
是一个工具类,提供了许多静态方法,根据用户选择返回不同的线程池实例。newScheduledThreadPool
方法新建了一个ScheduledThreadPoolExecutor
实例。
ScheduledThreadPoolExecutor
的继承关系如下:
可以看到,ScheduledThreadPoolExecutor
继承了ThreadPoolExecutor
并实现了ScheduledExecutorService
。换句话说,ScheduledThreadPoolExecutor
的功能实现依赖于线程池。这一点从它的构造函数可以看出:
1 | public ScheduledThreadPoolExecutor(int corePoolSize, |
可以看到,ScheduledThreadPoolExecutor
的构造函数中调用了父类ThreadPoolExecutor
的构造函数。唯一特殊的地方就是它传入的阻塞队列为一个在ScheduledThreadPoolExecutor
中实现的DelayedWorkQueue
,这个队列是实现定时任务的关键,我们在后面详述。
schedule方法
两个schedule
方法大同小异,我们以传入Runnable
参数的方法为例,其代码如下所示:
1 | public ScheduledFuture<?> schedule(Runnable command, |
scheduleAtFixedRate方法
scheduleAtFixedRate
方法相对任务的起始时间点固定频率调用。代码如下:
1 | public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, |
scheduleWithFixedDelay方法
scheduleWithFixedDelay
方法相对任务的结束时间点固定频率调用。代码如下:
1 | public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, |
ScheduledFutureTask
我们在之前的文章ThreadPoolExecutor中对线程池的运行原理进行了详细的描述。线程池的工作线程通过getTask
方法获取任务并执行,getTask
方法调用workQueue
的E poll(long timeout, TimeUnit unit)
或者E take()
的方法从工作队列中获取任务。
我们在一般情况下使用LinkedBlockingQueue
作为线程池的队列,这也意味着线程池对于任务的执行顺序是先进先出的。
ScheduledThreadPoolExecutor
的特殊性就在于采用了DelayedWorkQueue
作为队列。DelayedWorkQueue
是一个堆实现的优先队列,它按照每个任务距离下次执行时间间隔的大小排序,时间间隔小的任务排在前面。
前面我们看到任务被包装成ScheduledFutureTask
,DelayedWorkQueue
根据ScheduledFutureTask
提供的比较函数实现排序。代码如下:
1 | ScheduledFutureTask(Runnable r, V result, long ns) { |
当线程池获得了DelayedWorkQueue
队列头部的任务后调用ScheduledFutureTask
的run
方法运行任务。代码如下:
1 | public void run() { |
https://blog.csdn.net/wo541075754/article/details/51556198
https://blog.csdn.net/xiangzhihong8/article/details/78995686