ThreadPool--ScheduledThreadPoolExecutor
ScheduledThreadPoolExecutor是一种类似Timer的定时器或者说是调度器,和Timer比起来主要有几点好处:
- 多线程的定时调度,timer是单线程的,每个timer实例只有一个工作线程。
- 由于继承自ThreadPoolExecutor,更具有灵活性和伸缩性。
- .没有timer那种线程泄露问题,timer调度的任务如果异常终止,那么整个timer都会被取消,无法执行其他任务。
ScheduledFutureTask是ScheduledThreadPoolExecutor的内部类,也是FutureTask的子类。它的isPeriodic() 决定这个任务是否需要重复执行。
Task重复执行的步骤
1 scheduleWithFixedDelay生成可重复执行的任务
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();
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;
}
上面代码生成的ScheduledFutureTask是周期性执行的任务,通过 delayedExecute(t)把它加入到阻塞队列中。并启动线程执行队列中的任务。
到此为止,一个用ScheduledFutureTask包装的任务提交到了线程池的工作队列中,等待线程取出后执行。任务被取出后,会先调用Runnable接口的run方法,那我们来看下run方法做了些什么
public void run() {
boolean periodic = isPeriodic();
if (!canRunInCurrentRunState(periodic))// 检查当前线程池状态是否需要取消
cancel(false);
else if (!periodic)// 如果不是周期性任务,直接调用父类FutureTask的run方法执行任务。
ScheduledFutureTask.super.run();
else if (ScheduledFutureTask.super.runAndReset()) {
//否则,调用父类run方法执行任务,但是不设置结果,以便计算出下次执行的时间后,重复执行。
setNextRunTime();
reExecutePeriodic(outerTask);
}
}
总结一下:
- ScheduledFutureTask表示可调度的异步任务,提交到ScheduledThreadPoolExecutor的任务都会被包装成这个类。
- ScheduledThreadPoolExecutor调度任务时会按照延迟时间来,延迟时间最先到期的任务会被首先调度,如果两个任务延迟时间相同,那么还有内部序列号来保证先入先出的顺序。
- ScheduledFutureTask被调度后,具体执行时,会判断自己是否是周期性任务。如果不是,任务执行一次;如果时,先执行任务,执行成功后,会算出下次触发事件(延迟时间),然后被再次放入ScheduledThreadPoolExecutor的任务队列中,等待下次被调度执行。