1. 继承逻辑
scheduledThreadPoolExecutor继承了ThreadPoolExecutor、同时实现了ScheduledExecutorService接口,ScheduledExecutorService接口主要是提供了schedule、
scheduleAtFixedRate、scheduleWithFixedDelay这些定时执行的方法
2. 核心方法
延迟delay时间后只执行一次该任务,无返回值
public ScheduledFuture<?> schedule(Runnable command,
long delay, TimeUnit unit);
延迟delay时间后只执行一次该任务,有返回值
public <V> ScheduledFuture<V> schedule(Callable<V> callable,
long delay, TimeUnit unit);
在延迟initialDelay时间后,以period为固定速率周期性执行任务,该方法指的是下次任务的开始执行时间和上次任务的开始执行时间是固定的period,无论上次任务的执行时间是多久,而scheduleAtFixedDelay方法则会保证下次任务的开始执行时间和上次任务执行的结束时间差值是固定的
period
|----initialDelay----|----period----|----period----|
run() run()
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
long initialDelay,
long period,
TimeUnit unit);
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
long initialDelay,
long delay,
TimeUnit unit);
3. 核心结构delayQueue
scheduleThreadPoolExecutor自己实现了DelayedWorkQueue
主要逻辑是: 定义了一个优先级队列,按照任务的执行时间排序,最先开始执行的任务排到前面, 当线程调用take方法去获取任务时,首先调用getDelay方法获取是否到达了该任务的执行时间, 如果到了直接返回,如果没有到则直接计算剩余时间diff,然后调用锁的awaitNanos(diff)方法 自我阻塞,到时间苏醒后返回任务,执行任务。这里使用leader线程用来表示获取队列中最早到期的任务的元素,leader线程阻塞有限的时间,非leader线程无限阻塞。本质是为了节省cpu时间。
每个被提交到线程池中的任务都会被包装成 ScheduleFutureTask类
private class ScheduledFutureTask<V>
extends FutureTask<V> implements RunnableScheduledFuture<V> {
在time这个时间点该任务应该被执行
private long time;
0 代表一次性执行的任务,> 0 代表固定频率执行,< 0 代表固定间隔执行
private final long period;
}
4. 周期性执行的核心逻辑
整个逻辑就是在执行任务前,判断是否是周期性执行任务,非周期性执行任务有返回值,
执行完后无其他行为。周期性任务没有返回值,在执行完当前这次任务后,
会重新设置下次任务的执行时间,根据peroid的正负分为time + p 或者 now() + p,
对应于fix-rate 和 fix-delay.
设置完再次执行时间后,将当前任务再次提交到delayQueue中。
public void run() {
判断 period 是否 = 0 等于0则非周期执行
boolean periodic = isPeriodic();
if (!canRunInCurrentRunState(periodic))
cancel(false);
else if (!periodic)
非周期性执行只执行一次
ScheduledFutureTask.super.run();
else if (ScheduledFutureTask.super.runAndReset()) {
在执行完runAndReset后,重新设置该任务下一次执行的时间点
setNextRunTime();
重新提交该任务到delay队列中,outerTask 引用指向自身
reExecutePeriodic(outerTask);
}
}