开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第14天,点击查看活动详情
前言
上一篇文章 一文带你搞懂 ScheduledExecutorService 定时任务类(一) 我们主要看了下 ScheduledExecutorService 定时任务类的应用 demo 、两个主要方法 scheduleAtFixedRate 和 scheduleWithFixedDelay。本篇文章,我们一起来深入地分析 ScheduledExecutorService 的源码。
ScheduledThreadPoolExecutor 类
首先,ScheduledExecutorService 是一个接口,它的具体实现类是 ScheduledThreadPoolExecutor,从源码中可以知道,ScheduledThreadPoolExecutor 继承了 ThreadPoolExecutor 类,同时实现了 ScheduledExecutorService 接口。因此,ScheduledThreadPoolExecutor 既是定时器,又是线程池
scheduleAtFixedRate 方法
首先,我们看一下 scheduleAtFixedRate 的实现:
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) {
if (command != null && unit != null) {
if (period <= 0L) {
throw new IllegalArgumentException();
} else {
ScheduledThreadPoolExecutor.ScheduledFutureTask<Void> sft = new ScheduledThreadPoolExecutor.ScheduledFutureTask(command, (Object)null, this.triggerTime(initialDelay, unit), unit.toNanos(period), sequencer.getAndIncrement());
RunnableScheduledFuture<Void> t = this.decorateTask((Runnable)command, sft);
sft.outerTask = t;
this.delayedExecute(t);
return t;
}
} else {
throw new NullPointerException();
}
}
该方法主要有以下几步:
- 校验任务和时间单位是否为空,若其中一个为空,则抛出空指针异常
- 校验执行周期 period 是否小于 1 ,如果是则抛出不合法参数异常
- 将任务、触发时间、执行周期、创建 ScheduledFutureTask 类,sequencer 计数器加一(并发安全的),sequencer 的作用是:如果两个任务执行时间相同,就可以根据该变量的大小来判断哪个任务先执行
- 将任务和 ScheduledFutureTask 组装成 RunnableScheduledFuture,执行decorateTask方法,主要作用是一个扩展点,允许用户修改和替换task
- 执行 delayedExecute 方法,它的作用是通过把任务放到一个 BlockingQueue 队列中,并且通过ensurePrestart 方法去保证至少有一个线程开始执行,ensurePrestart 是线程池的方法,它保证有线程能启动去执行任务,有关线程池的原理,在这里就不过多赘述
上述五个步骤,就将一个任务放入到线程池中了,具体要如何周期性运行,是通过 run 方法来执行的:
代码有三个分支:
- 首先是判断当前任务是否需要执行,如果不需要,则取消
- 如果是一次性任务,就执行 ScheduledFutureTask.super.run() 这个方法,即执行我们重新的Runnable的任务
- 如果是周期任务,就执行 ScheduledFutureTask.super.runAndReset() ,该方法执行逻辑为:获取下次执行的时间,然后再将任务重新加入到 BlockingQueue 队列,最后调用任务
总结
本文主要讲解了 ScheduledExecutorService 实现定时任务线程池的源码,核心其实主要在于线程池 + 任务队列 + 计算下一次执行时间的组合应用。感谢大家的支持~