Java Core 「16」J.U.C Executor 框架之 ScheduledThreadPoolExecutor
摘要
前面《Java Core 「15」J.U.C Executor 框架》中学习了 ThreadPoolExecutor,其基于 Executor 框架,实现了线程池功能。那 ScheduledThreadPoolExecutor 又是解决什么问题的呢?从前面的文章中,我们了解到,提交到线程池的任务都是一次性的,即执行完就结束了。ScheduledThreadPoolExecutor 解决的是周期性或延迟任务的线程池。
相比于 ThreadPoolExecutor,ScheduledThreadPoolExecutor 有什么特点呢?
可接受周期性任务,例如每隔 3s 执行一次,延迟任务,例如 5s 后执行;也可以接受普通的任务(由父类 ThreadPoolExecutor 执行)。
实现了特殊的阻塞队列 DelayedWorkQueue,无界延迟队列的一种,用来存储周期性或延时任务。
线程池 shutdown 之后,周期性或延时任务是否可继续执行可通过 run-after-shutdown 参数配置。
接下来,我们将详细地学习这些特点。
01-周期性任务 & 延时任务
ScheduledThreadPoolExecutor 继承自 ThreadPoolExecutor,并实现了 ScheduledExecutorService 接口。而正是后者定义了周期性任务执行(scheduleAtFixedRate / scheduleWithFixedDelay 方法)和延时任务执行(schedule 方法)。
schedule 方法共有两个版本,区别在于第一个参数是 Runnable 还是 Callable。ScheduledFutureTask 继承自 FutureTask,当入参为 Runnable 时,会被Executors.*callable*(runnable, null);
封装,赋值给继承自 FutureTask 的callable
变量。
schedule 方法大致上可以分为两步:
执行 decorateTask,将任务封装为 RunnableScheduledFuture。
执行 delayedExecute,其源码如下:
private void delayedExecute(RunnableScheduledFuture<?> task) { if (isShutdown()) reject(task); /** 若线程池已 shutdown,则拒绝任务 */ else { /** 线程池在运行,任务添加到阻塞队列中,取到的是 DelayedWorkQueue */ super.getQueue().add(task); /** 双重检查,若线程池当前不能执行任务,则取消任务 * canRunInCurrentRunState 包含了线程池 shutdown 后, * 对 run-after-shutdown 参数的判断逻辑 */ if (!canRunInCurrentRunState(task) && remove(task)) task.cancel(false); else /** ThreadPoolExecutor 中的方法,工作线程小于 corePoolSize 时,添加 Worker */ ensurePrestart(); } }
scheduleAtFixedRate 方法以固定的频率周期性执行任务,其源码如下:
scheduleWithFixedDelay 以固定的延时周期性执行任务,其源码如下:
scheduleAtFixedRate / scheduleWithFixedDelay 方法与前面的 schedule 方法步骤大致一样,同样分为两步:
将 Runnable 任务封装为 ScheduledFutureTask 对象;有两点需要注意:a) 没有 Callable 对象的接口,不过这也比较容易理解,周期性执行的任务会重复多次,返回值不太重要。b) 两个函数在 period 入参上取值有区别,前者为正值,后者为负值。
执行 delayedExecute 方法,与延时任务一致。
02-内部类
ScheduledThreadPoolExecutor 中包含两个内部类,ScheduledFutureTask 和 DelayedWorkQueue,前者是周期性任务对象,后者是存储周期性任务的阻塞队列。
ScheduledFutureTask 继承了 FutureTask,是一个异步运算任务;并且实现了 Runnable、Future、Delayed 接口,说明它是一个可以延迟执行的异步运算任务。
DelayedWorkQueue 继承了 AbstractQueue,并且为了契合父类 ThreadPoolExecutor,它还实现了 BlockingQueue 接口。在其内部,只允许存储 RunnableScheduledFuture 类型的对象, 而 ScheduledFutureTask 也实现了 RunnableScheduledFuture 接口。
ScheduledFutureTask 中与时间相关的两个关键属性:
既然 ScheduledFutureTask 实现了 FutureTask 接口,其关键方法之一就是 run 方法,在线程执行时会调用:
03-run-after-shutdown 参数
ScheduledThreadPoolExecutor 与 ThreadPoolExecutor 的不同之处是可以配置周期性、延迟任务在线程池 shutdown 之后,仍然运行。配置这种行为的参数被称为是 run-after-shutdown 参数。
历史文章推荐
Java Core 「15」J.U.C Executor 框架
Java Core 「14」J.U.C 线程池 -Future & FutureTask
Java Core 「13」ReentrantReadWriteLock 再探析
Java Core 「12」ReentrantLock 再探析
版权声明: 本文为 InfoQ 作者【Samson】的原创文章。
原文链接:【http://xie.infoq.cn/article/e28cc50eed16bdafe437f236b】。文章转载请联系作者。
评论