本篇博客分析下ScheduledThreadPoolExecutor的源码。
ScheduledThreadPoolExecutor继承ThreadPoolExecutor,
实现ScheduledExecutorService接口。
在普通线程池的基础上,增加了延迟、周期性执行任务等能力。
一、schedule
我们先来看一个延迟执行任务的接口源码:
public ScheduledFuture<?> schedule(Runnable command,
long delay,
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
//decorateTask函数,默认返回传入的参数ScheduledFutureTask
//这个可以由子类复写,实现特定的需求
RunnableScheduledFuture<Void> t = decorateTask(command,
//ScheduledFutureTask为ScheduledThreadPoolExecutor中定义的内部类
new ScheduledFutureTask<Void>(command, null,
triggerTime(delay, unit),
sequencer.getAndIncrement()));
//延迟执行
delayedExecute(t);
return t;
}
我们再来看一个周期性执行任务的接口源码:
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
long initialDelay,
long period,
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
if (period <= 0L)
throw new IllegalArgumentException();
//同样创建一个ScheduledFutureTask, 只不过参数多了一些
ScheduledFutureTask<Void> sft =
new ScheduledFutureTask<Void>(command,
null,
triggerTime(initialDelay, unit),
unit.toNanos(period),
sequencer.getAndIncrement());
//也调用一下decorateTask
RunnableScheduledFuture<Void> t = decorateTask(command, sft);
sft.outerTask = t;
//最后延迟执行
delayedExecute(t);
return t;
}
通过对比schedule和scheduleAtFixedRate,不难发现:两个函数的实现几乎一致,
同样是利用参数构建出ScheduledFutureTask,然后调用delayedExecute延迟处理。
不同的地方是:两个函数构建ScheduledFutureTask时的参数有差别,
scheduleAtFixedRate构建ScheduledFutureTask时,多传入了一个参数作为周期。
二、delayedExecute
现在我们继续跟进delayedExecute函数:
private void delayedExecute(RunnableScheduledFuture<?> task) {
//shutdown时,拒绝task
if (isShutdown())
reject(task);
else {
//将任务加入到队列中
super.getQueue().add(task);
//加入队列后,需再次判断是否shutdown
if (isShutdown() &&
//判断shutdown时,是否运行周期或延迟的任务
//默认情况下,shutdown后可以运行延迟执行的任务,不能再运行周期性任务
//可以通过接口改变默认行为
!canRunInCurrentRunState(task.isPeriodic()) &&
//无法执行任务时,则移除并取消
remove(task))
task.cancel(false);
else
//确保任务开始执行
ensurePrestart();
}
}
从上述代码来看,delayedExecute主要的作用是:
判断任务是否应该被加入到工作队列中,以及是否应该执行。
继续看一下ensurePrestart的代码:
void ensurePrestart() {
int wc = workerCountOf(ctl.get());
if (wc < corePoolSize)
addWorker(null, true);
else if (wc == 0)
addWorker(null, false);
}