任务添加
ScheduledThreadPoolExecutor对任务的添加提供了大量的方法。
- 重写ThreadPoolExecutor
// 执行任务command public void execute(Runnable command) { schedule(command, 0, TimeUnit.NANOSECONDS); }
- 重写AbstractExecutorService
// 执行任务task public Future<?> submit(Runnable task) { return schedule(task, 0, TimeUnit.NANOSECONDS); } // 执行任务task public <T> Future<T> submit(Runnable task, T result) { return schedule(Executors.callable(task, result), 0, TimeUnit.NANOSECONDS); } // 执行任务task public <T> Future<T> submit(Callable<T> task) { return schedule(task, 0, TimeUnit.NANOSECONDS); }
- 实现ScheduledExecutorService接口
// 延迟执行任务command,延迟时间由delay和unit决定,只执行一次 public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) { // 检查参数 if (command == null || unit == null) throw new NullPointerException(); /** * 使用triggerTime()计算执行时间,创建一个RunnableScheduledFuture对象 * 默认的decorateTask()方法什么都没做,直接将传入的ScheduledFutureTask返回了 */ RunnableScheduledFuture<?> t = decorateTask(command, new ScheduledFutureTask<Void>(command, null, triggerTime(delay, unit))); // 延迟执行任务 delayedExecute(t); return t; } // 延迟执行会产生执行结果任务command,延迟时间由delay和unit决定;只执行一次 public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) { // 检查参数 if (callable == null || unit == null) throw new NullPointerException(); /** * 使用triggerTime()计算执行时间,创建一个RunnableScheduledFuture对象 * 默认的decorateTask()方法什么都没做,直接将传入的ScheduledFutureTask返回了 */ RunnableScheduledFuture<V> t = decorateTask(callable, new ScheduledFutureTask<V>(callable, triggerTime(delay, unit))); // 延迟执行任务 delayedExecute(t); return t; } /** * 该方法在initialDelay时长后第一次执行任务,以后每隔period时长,再次执行任务。 * 注意,period是从任务开始执行算起的。 * 开始执行任务后,定时器每隔period时长检查该任务是否完成, * 如果完成则再次启动任务,否则等该任务结束后才再次启动任务 */ public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { // 检查参数 if (command == null || unit == null) throw new NullPointerException(); if (period <= 0) throw new IllegalArgumentException(); // 使用triggerTime()计算执行时间,创建一个ScheduledFutureTask对象 ScheduledFutureTask<Void> sft = new ScheduledFutureTask<Void>(command, null, triggerTime(initialDelay, unit), unit.toNanos(period)); // 使用decorateTask()对ScheduledFutureTask对象进行装饰 RunnableScheduledFuture<Void> t = decorateTask(command, sft); sft.outerTask = t; // 执行任务 delayedExecute(t); return t; } /** * 该方法在initialDelay时长后第一次执行任务, * 以后每当任务执行完成后,等待delay时长,再次执行任务 */ public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) { // 检查参数 if (command == null || unit == null) throw new NullPointerException(); if (delay <= 0) throw new IllegalArgumentException(); // 使用triggerTime()计算执行时间,创建一个ScheduledFutureTask对象 ScheduledFutureTask<Void> sft = new ScheduledFutureTask<Void>(command, null, triggerTime(initialDelay, unit), unit.toNanos(-delay)); RunnableScheduledFuture<Void> t = decorateTask(command, sft); sft.outerTask = t; delayedExecute(t); return t; }
任务执行
当ScheduledThreadPoolExecutor将添加的任务都放入DelayedWorkQueue队列中后,如果线程池符合运行状态,会调用ensurePrestart()预启动Worker线程,这个方法位于父类ThreadPoolExecutor中,我们回顾一下:
void ensurePrestart() {
// 获取workerCount
int wc = workerCountOf(ctl.get());
// 如果workerCount小于corePoolSize
if (wc < corePoolSize)
// 添加核心线程
addWorker(null, true);
else if (wc == 0)
// 添加非核心线程
addWorker(null, false);
}
- 添加Worker线程后,它最终会启动Worker自线程,即调用Worker的run()方法,而run()方法则会调用ThreadPoolExecutor的runWorker(Worker w)方法并将Worker自己传入。
- 该方法的while循环条件中会不断取出Worker的firstTask,如果firstTask为null就会调用getTask()方法获取Runnable任务对象并调用其run()方法以实现任务的运行。在ScheduledThreadPoolExecutor中,由于所有Worker都是预创建的,firstTask都为null,因此会调用getTask()方法获取任务;
- 而getTask()方法则实现了从等待队列中获取任务:
private Runnable getTask() {
// 用于记录poll()方法是否超时
boolean timedOut = false; // Did the last poll() time out?
retry:
// 无限循环
for (; ; ) {
// 获取ctl和runState
int c = ctl.get();
int rs = runStateOf(c);
// 检查状态
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
/**
* 如果runState为STOP、TIDYING或TERMINATED
* 或者runState为SHUTDOWN,且workerQueue为空
* 将workerCount减1,返回null结束运行
*/
decrementWorkerCount();
return null;
}
// 记录worker是否能够被移除
boolean timed; // Are workers subject to culling?
// 无限循环
for (; ; ) {
// 获取workerCount
int wc = workerCountOf(c);
/**
* 判断当前Worker是否可以被移除,即当前Worker是否可以一直等待任务。
* 如果allowCoreThreadTimeOut为true,或者workerCount大于核心线程数,
* 则当前线程是有超时时间的(keepAliveTime),无法一直等待任务
*/
timed = allowCoreThreadTimeOut || wc > corePoolSize;
/**
* workerCount小于等于核心线程数,且没有超时,则跳出内层循环
*/
if (wc <= maximumPoolSize && !(timedOut && timed))
break;
// 否则表示已超时,将workerCount减1,如果成功直接返回null
if (compareAndDecrementWorkerCount(c))
return null;
// 走到这里说明上一步workerCount减1失败了,重新读取ctl
c = ctl.get(); // Re-read ctl
// 如果与之前的runState不同,表示线程池状态发生改变了,跳出到外层循环重试
if (runStateOf(c) != rs)
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
try {
/**
* 根据线程是否会超时来分别调用相应的方法,
* poll()方法附带超时机制;take()方法没有超时机制,并且可能会阻塞等待
*/
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
// 如果获取到的任务不为null则返回
if (r != null)
return r;
// 走到这里表示获取操作超时了
timedOut = true;
} catch (InterruptedException retry) {
// 被中断,可能是超时等待过程中被中断了
timedOut = false;
}
}
}
- 因为ScheduledThreadPoolExecutor种keepAliveTime默认是0,所以getTask方法种实际调用的是take()方法,而任务的延时执行,正是由DelayedWorkQueue的出队操作take()实现的,它会根据任务的延迟时间决定是否可以出队,从而达到延迟执行任务。
- 任务真实执行是调用的任务ScheduledFutureTask的run()方法
/**
* Overrides FutureTask version so as to reset/requeue if periodic.
* 任务的具体执行
*/
public void run() {
// 任务是否是周期性的
boolean periodic = isPeriodic();
// 判断当前周期性任务是否可以执行
if (!canRunInCurrentRunState(periodic))
// 如果不能执行,就取消当前任务
cancel(false);
else if (!periodic)
// 如果不是周期性任务,直接执行即可
ScheduledFutureTask.super.run();
else if (ScheduledFutureTask.super.runAndReset()) {
// 否则执行任务并重置,然后计算并设置下次执行时间
setNextRunTime();
// 重新添加下一次任务,即将当前ScheduledFutureTask重新入队
reExecutePeriodic(outerTask);
}
}
重复性任务的执行
根据上面的讲解ScheduledFutureTask中run()方法的内容,当任务执行完成并线程池状态重置成功后,会调用 setNextRunTime() 计算并设置任务的下一次执行执行时间,然后调用reExecutePeriodic(outerTask)重新添加下一次任务,该方法源码如下:
/**
* Requeues a periodic task unless current run state precludes it.
* Same idea as delayedExecute except drops task rather than rejecting.
*
* 周期性任务的重新入队
*
* @param task the task
*/
void reExecutePeriodic(RunnableScheduledFuture<?> task) {
// 判断当前线程池状态是否可以运行周期性任务
if (canRunInCurrentRunState(true)) {
// 任务重新入队
super.getQueue().add(task);
/**
* 重新检查当前线程池状态是否可以运行周期性任务,如果不行就从队列中移除任务
* 并取消任务的执行
*/
if (!canRunInCurrentRunState(true) && remove(task))
task.cancel(false);
else
// 如果可以运行就预启动Worker线程
ensurePrestart();
}
}
任务的取消
代码比较简单,调用了父类FutureTask的cancel(boolean mayInterruptIfRunning)方法,内部会根据传入参数决定是否对任务线程进行中断。在任务成功取消后,会根据removeOnCancel和heapIndex决定是否将任务从队列中移除。这里讲解一下removeOnCancel成员变量,它属于ScheduledThreadPoolExecutor类,用于控制任务在取消时是否从队列移除,默认为false;
// 取消任务的执行
public boolean cancel(boolean mayInterruptIfRunning) {
// 调用父类方法取消任务的执行
boolean cancelled = super.cancel(mayInterruptIfRunning);
// 如果取消成功,且removeOnCancel策略为true,任务的heapIndex大于等于0
if (cancelled && removeOnCancel && heapIndex >= 0)
// 则将任务从队列中移除
remove(this);
// 返回是否取消成功的结果
return cancelled;
}