Java并发编程的艺术:(10) Executor 框架(二)
文章目录
ScheduledThreadPoolExecutor 简介
ScheduledThreadPoolExecutor 继承自 ThreadPoolExecutor。它主要用来在给定的延迟之后运行任务,或者定期执行任务。ScheduledThreadPoolExecutor 的功能与 Timer 类似,但ScheduledThreadPoolExecutor 功能更强大、更灵活。Timer 对应的是单个后台线程,而 ScheduledThreadPoolExecutor 可以在构造函数中指定多个对应的后台线程数。
ScheduledThreadPoolExecutor 的运行机制
ScheduledThreadPoolExecutor 的执行主要分为两大部分。
- 当调用 ScheduledThreadPoolExecutor 的 scheduleAtFixedRate() 方法或者 scheduleWithFixedDelay() 方法时,会向 ScheduledThreadPoolExecutor 的 DelayQueue 添加一个实现了 RunnableScheduledFutur 接口的 ScheduledFutureTask。
- 线程池中的线程从 DelayQueue 中获取 ScheduledFutureTask,然后执行任务。
ScheduledThreadPoolExecutor 的实现
前面我们提到过,ScheduledThreadPoolExecutor 会把调度的任务(ScheduledFutureTask) 放到一个 DelayQueue 中。
ScheduledFutureTask 主要包含 3 个成员变量,如下。
- long 型成员变量 time, 表示这个任务将要被执行的具体时间。
- long 型成员变量 squenceNumber,表示这个任务被添加到 ScheduledThreadPoolExecutor 中的序号。
- long 型成员变量 period,表示任务执行的间隔周期。
DelayQueue 封装了一个 PriorityQueue,这个 PriorityQueue 会对队列中的 ScheduledFutureTask 进行排序。排序时,time 小的排在前面(时间早的任务将被先执行)。如果两个 ScheduledFutrueTask 的 time 相同,就比较 sequenceNumber,sequenceNumber 小的排在前面(也就是说,如果两个任务的执行时间相同,那么先提交的任务将被先执行)。
上图是 ScheduledThreadPoolExecutor 中的线程 1 执行某个周期任务的 4 个步骤。
下面是对这 4 个步骤的说明。
- 线程 1 从 DelayQueue 中获取已到期的 ScheduledFutureTask(DelayQueue.take())。到期任务是指 ScheduledFutrueTask 的 time 大于当前时间。
- 线程 1 执行这个 ScheduledFutureTask。
- 线程 1 修改 ScheduledFutureTask 的 time 变量为下次将要被执行的时间。
- 线程 1 把这个修改 time 之后的 ScheduledFutureTask 放回 DelayQueue 中(DelayQueue.add())。
FutureTask 应用
Future 接口实现 Future 接口的 FutureTask 类,代表异步计算的结果。
FutureTask 简介
FutureTask 除了实现 Future 接口外,还实现了 Runnable 接口。因此,FutureTask 可以交给 Executor 执行,也可以由调用线程直接执行(FutureTask.run())。根据 FutureTask.run()方法被执行的时机,FutureTask 可以处于下面 3 种状态。
- 未启动。FutureTask.run() 方法还没有被执行之前,FutureTask 处于未启动状态,当创建一个FutureTask,且没有执行 Future Task.run() 方法之前,这个FutureTask处于未启动状态。
- 已启动。FutureTask.run() 方法被执行的过程中,FutureTask 处于已启动状态。
- 已完成。FutureTask.run() 方法执行完后正常结束,或被取消(FutureTask.cancle(…)),或执行 FutureTask.run() 方法时抛出异常而异常结束,FutureTask 处于已完成状态。
FutureTask 的使用
可以把 FutureTask 交给 Executor 执行;也可以通过 ExecutorService.submit(…) 方法返回一个 FutureTask,然后执行 FutureTask.get() 方法或 FutureTask.cancle(…)方法。除此以外,还可以单独使用 FutureTask。
参考
- 《Java 并发编程的艺术》