1、执行过程
1、使用 ThreadPoolExecutor 线程池来提交、运行任务,并实现 ScheduledExecutorService 接口类,
该类继承于 ExecutorService,使用的队列是 DelayedWorkQueue,添加了几个定时方法
2、将任务封装成 ScheduledFutureTask 类型,添加到延迟队列中
2、ScheduledFutureTask
实现了 RunnableScheduledFuture 多个方法,如 isPeriodic()、getDelay(),并重新实现 run 运行任务逻辑,作了一层封装
public void run() {
boolean periodic = isPeriodic();
//1.SHUTDOWN 状态下,周期性任务会停止,只执行一次的任务会继续执行完毕
if (!canRunInCurrentRunState(periodic))
cancel(false);
else if (!periodic)
//2.直接执行任务
ScheduledFutureTask.super.run();
//3.直接执行任务,并重置
else if (ScheduledFutureTask.super.runAndReset()) {
//4.设置下次需要执行的时间
setNextRunTime();
//5.再次放入延迟队列,等待下次执行
reExecutePeriodic(outerTask);
}
}
void reExecutePeriodic(RunnableScheduledFuture<?> task) {
if (canRunInCurrentRunState(true)) {
//1.添加到延迟任务队列中
super.getQueue().add(task);
if (!canRunInCurrentRunState(true) && remove(task))
task.cancel(false);
else
//2.判断是否需要添加线程
ensurePrestart();
}
}
3、下次执行的时间
//两种情况都是在上次任务执行完毕之后执行, 不同的是:
//一种: 参考的是固定的频率, 10:00:00, 10:00:05, 10:00:10, 如果上次任务执行时间在间隔内, 那么按照以上频率去周期执行; 如果超过间隔, 那么上次任务执行完毕, 下次任务会立即执行; 类似于火车班次, 无论路上堵车了, 班次还是会定点开
//一种: 参考的是上次任务结束的时间, 根据这个时间, 往后推周期时间去执行
private void setNextRunTime() {
long p = period;
if (p > 0)
time += p;
else
time = triggerTime(-p);
}