「JUC 系列」- ScheduledThreadPoolExecutor 简介

572 阅读2分钟

这是我参与8月更文挑战的第24天,活动详情查看:8月更文挑战

这个类在实际项目中基本不会被用到,因为有其他方案选择比如 quartz,但作为 JUC 包下的线程池,我们还是可以了解一下的

ScheduledThreadPoolExecutor 继承自 ThreadPoolExecutorScheduledThreadPoolExecutor的状态管理、入队操作、拒绝操作等都是继承于 ThreadPoolExecutorScheduledThreadPoolExecutor 主要是提供了周期任务和延迟任务相关的操作。

ThreadPoolExecutor 相比,它还具有以下几种特性:

  • 使用专门的任务类型 ScheduledFutureTask 来执行周期任务,也可以接收不需要时间调度的任务(这些任务通过 ExecutorService来执行)。
  • 使用专门的存储队列 DelayedWorkQueue 来存储任务,DelayedWorkQueue 是无界延迟队列 DelayQueue的一种。相比ThreadPoolExecutor 也简化了执行机制。
  • 支持可选的 run-after-shutdown 参数,在池被关闭之后支持可选的逻辑来决定是否继续运行周期或延迟任务。并且当任务重新提交操作与 shutdown操作重叠时,复查逻辑也不相同。

二 ScheduledThreadPoolExecutor 详解

ScheduledThreadPoolExecutor 的运行逻辑而言,大致可以表述为:

  • 首先将 Runnable/Callable 封装为 ScheduledFutureTask,延迟时间 time 作为比较属性。
  • 然后加入 DelayedWorkQueue 队列中,每次取出队首延迟最小的任务,超时等待,然后执行,如果执行所需时间相同则先提交的任务将被先执行,ScheduledFutureTasksquenceNumber 变量小的先执行。
  • 最后判断是否为周期任务,然后将其重新加入 DelayedWorkQueue队列中。

1)运行机制

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

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

ScheduledThreadPoolExecutor** 为了实现周期性的执行任务,对 ****ThreadPoolExecutor**做了如下修改:

  • 使用 DelayQueue 作为任务队列;
  • 获取任务的方不同
  • 执行周期任务后,增加了额外的处理

2)ScheduledThreadPoolExecutor 执行周期任务的步骤

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