9.1 介绍
ThreadPoolExecutor
只是Executors工具类的一部分功能。下面来介绍另外一部分功能,也就是ScheduledThreadPoolExecutor
的实现,这是一个可以在指定一定延迟时间后或者定时进行任务调度执行的线程池。
9.2 类图介绍
Executors
其实是个工具类,它提供了好多静态方法,可根据用户的选择返回不同的线程池实例。ScheduledThreadPoolExecutor
继承了ThreadPoolExecutor
并实现了ScheduledExecutorService
接口。线程池队列是DelayedWorkQueue
,其和DelayedQueue
类似,是一个延迟队列。
ScheduledFutureTask
是具有返回值的任务,继承自FutureTask
。FutureTask的内部有一个变量state
用来表示任务的状态,一开始状态为NEW,所有状态为
private static final int NEW = 0; // 初始状态
private static final int COMPLETING = 1; // 执行中状态
private static final int NORMAL = 2; // 正常运行结束状态
private static final int EXCEPTIONAL = 3; // 运行中异常
private static final int CANCELLED = 4; // 任务被取消
private static final int INTERRUPTING = 5; // 任务正在被中断
private static final int INTERRUPTED = 6; // 任务已经被中断
// 可能的任务状态转换路径:
NEW -> COMPLETING -> NORMAL / /初始状态->执行中->正常结束
NEW -> COMPLETING -> EXCEPTIONAL/ /初始状态->执行中->执行异常
NEW -> CANCELLED/ /初始状态->任务取消
NEW -> INTERRUPTING -> INTERRUPTED/ /初始状态->被中断中->被中断
ScheduledFutureTask
内部还有一个变量period
用来表示任务的类型,任务类型如下:
- period=0,说明当前任务是一次性的,执行完毕后就退出了
- period为负数,说明当前任务为fixed-delay任务,是固定延迟的定时可重复执行任务。
- period为正数,说明当前任务为fixed-rate任务,是固定频率的定时可重复执行任务。
9.3 原理剖析
-
schedule(Runnable command, long delay, TimeUnit unit)
:该方法的作用是提交一个延迟执行的任务,任务从提交时间算起延迟单位为unit的delay时间后开始执行。提交的任务不是周期性任务,任务只会执行一次。 -
scheduleWithFixedDelay(Runnable command, long initialDelay, long delay,TimeUnit unit)
:该方法的作用是,当任务执行完毕后,让其延迟固定时间后再次运行(fixed-delay任务)。其中initialDelay
表示提交任务后延迟多少时间开始执行任务command,delay
表示当任务执行完毕后延长多少时间后再次运行command任务,unit
是initialDelay
和delay
的时间单位。任务会一直重复运行直到任务运行中抛出了异常,被取消了,或者关闭了线程池。当添加一个任务到延迟队列后,等待
initialDelay
时间,任务就会过期,过期的任务就会被从队列移除,并执行。执行完毕后,会重新设置任务的延迟时间,然后再把任务放入延迟队列,循环往复。需要注意的是,如果一个任务在执行中抛出了异常,那么这个任务就结束了,但是不影响其他任务的执行。scheduledExecutorService.scheduleWithFixedDelay开始执行----Wed May 27 17:04:53 CST 2020 【FIRST】 本次执行任务时间为3秒 scheduledExecutorService.scheduleWithFixedDelay开始执行----Wed May 27 17:04:58 CST 2020 本次执行任务时间为5秒 scheduledExecutorService.scheduleWithFixedDelay开始执行----Wed May 27 17:05:05 CST 2020 本次执行任务时间为1秒 scheduledExecutorService.scheduleWithFixedDelay开始执行----Wed May 27 17:05:08 CST 2020 本次执行任务时间为2秒 scheduledExecutorService.scheduleWithFixedDelay开始执行----Wed May 27 17:05:12 CST 2020
-
scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnitunit)
:该方法相对起始时间点以固定频率调用指定的任务,当把任务提交到线程池并延迟initialDelay
时间(时间单位为unit)后开始执行任务command。然后从initialDelay+period
时间点再次执行,而后在initialDelay + 2 * period
时间点再次执行,循环往复,直到抛出异常或者调用了任务的cancel方法取消了任务,或者关闭了线程池。相对于fixed-delay任务来说,fixed-rate方式执行规则为,时间为initdelday +n*period时启动任务,但是如果当前任务还没有执行完,下一次要执行任务的时间到了,则不会并发执行,下次要执行的任务会延迟执行,要等到当前任务执行完毕后再执行。
scheduledExecutorService.scheduleWithFixedDelay开始执行----Wed May 27 17:07:09 CST 2020 【FIRST】 本次执行任务时间为5秒 scheduledExecutorService.scheduleWithFixedDelay开始执行----Wed May 27 17:07:14 CST 2020 本次执行任务时间为4秒 scheduledExecutorService.scheduleWithFixedDelay开始执行----Wed May 27 17:07:18 CST 2020 本次执行任务时间为4秒 scheduledExecutorService.scheduleWithFixedDelay开始执行----Wed May 27 17:07:22 CST 2020 本次执行任务时间为2秒 scheduledExecutorService.scheduleWithFixedDelay开始执行----Wed May 27 17:07:24 CST 2020
9.4 总结
本章讲解了ScheduledThreadPoolExecutor的实现原理,ScheduledThreadPoolExecutor其内部使用DelayQueue来存放具体任务。任务分为三种,其中一次性执行任务执行完毕就结束了,fixed-delay任务保证同一个任务在多次执行之间间隔固定时间,fixed-rate任务保证按照固定的频率执行。任务类型使用period的值来区分。