Java并发编程的艺术:(10) Executor 框架(二)

ScheduledThreadPoolExecutor 简介

ScheduledThreadPoolExecutor 继承自 ThreadPoolExecutor。它主要用来在给定的延迟之后运行任务,或者定期执行任务。ScheduledThreadPoolExecutor 的功能与 Timer 类似,但ScheduledThreadPoolExecutor 功能更强大、更灵活。Timer 对应的是单个后台线程,而 ScheduledThreadPoolExecutor 可以在构造函数中指定多个对应的后台线程数。

ScheduledThreadPoolExecutor 的运行机制

ScheduledThreadPoolExecutor 的执行主要分为两大部分。

  1. 当调用 ScheduledThreadPoolExecutor 的 scheduleAtFixedRate() 方法或者 scheduleWithFixedDelay() 方法时,会向 ScheduledThreadPoolExecutor 的 DelayQueue 添加一个实现了 RunnableScheduledFutur 接口的 ScheduledFutureTask。
  2. 线程池中的线程从 DelayQueue 中获取 ScheduledFutureTask,然后执行任务。

ScheduledThreadPoolExecutor 的实现

前面我们提到过,ScheduledThreadPoolExecutor 会把调度的任务(ScheduledFutureTask) 放到一个 DelayQueue 中。

ScheduledFutureTask 主要包含 3 个成员变量,如下。

  1. long 型成员变量 time, 表示这个任务将要被执行的具体时间。
  2. long 型成员变量 squenceNumber,表示这个任务被添加到 ScheduledThreadPoolExecutor 中的序号。
  3. long 型成员变量 period,表示任务执行的间隔周期。

DelayQueue 封装了一个 PriorityQueue,这个 PriorityQueue 会对队列中的 ScheduledFutureTask 进行排序。排序时,time 小的排在前面(时间早的任务将被先执行)。如果两个 ScheduledFutrueTask 的 time 相同,就比较 sequenceNumber,sequenceNumber 小的排在前面(也就是说,如果两个任务的执行时间相同,那么先提交的任务将被先执行)。
Java并发编程的艺术:(10) Executor 框架(二)
上图是 ScheduledThreadPoolExecutor 中的线程 1 执行某个周期任务的 4 个步骤。

下面是对这 4 个步骤的说明。

  1. 线程 1 从 DelayQueue 中获取已到期的 ScheduledFutureTask(DelayQueue.take())。到期任务是指 ScheduledFutrueTask 的 time 大于当前时间。
  2. 线程 1 执行这个 ScheduledFutureTask。
  3. 线程 1 修改 ScheduledFutureTask 的 time 变量为下次将要被执行的时间。
  4. 线程 1 把这个修改 time 之后的 ScheduledFutureTask 放回 DelayQueue 中(DelayQueue.add())。

FutureTask 应用

Future 接口实现 Future 接口的 FutureTask 类,代表异步计算的结果。

FutureTask 简介

FutureTask 除了实现 Future 接口外,还实现了 Runnable 接口。因此,FutureTask 可以交给 Executor 执行,也可以由调用线程直接执行(FutureTask.run())。根据 FutureTask.run()方法被执行的时机,FutureTask 可以处于下面 3 种状态。

  1. 未启动。FutureTask.run() 方法还没有被执行之前,FutureTask 处于未启动状态,当创建一个FutureTask,且没有执行 Future Task.run() 方法之前,这个FutureTask处于未启动状态。
  2. 已启动。FutureTask.run() 方法被执行的过程中,FutureTask 处于已启动状态。
  3. 已完成。FutureTask.run() 方法执行完后正常结束,或被取消(FutureTask.cancle(…)),或执行 FutureTask.run() 方法时抛出异常而异常结束,FutureTask 处于已完成状态。

FutureTask 的使用

可以把 FutureTask 交给 Executor 执行;也可以通过 ExecutorService.submit(…) 方法返回一个 FutureTask,然后执行 FutureTask.get() 方法或 FutureTask.cancle(…)方法。除此以外,还可以单独使用 FutureTask。

参考

  • 《Java 并发编程的艺术》