1、继承关系图
2、简介
ScheduledThreadPoolExecutor是ThreadPoolExecutor的子类,继承了父类对线程的管理维护功能,通过还可以执行延迟和定时任务。
相对于ThreadPoolExecutor 的变化:
(1)使用内部类ScheduledFutureTask封装任务
(2)使用内部类DelayedWorkQueue作为线程池队列
(3)onShutdown方法基于参数配置化去处理shutdown后的任务
(4)提供decorateTask方法作为ScheduledFutureTask的修饰方法,以便使用者进行扩展关于 ThreadPoolExecutor 部分的详细介绍:线程池:ThreadPoolExecutor 源码解析
ScheduledThreadPoolExecutor
的实现原理,其内部使用的DelayQueue
来存放具体任务,其中任务分为三种,其中一次性执行任务执行完毕就结束了,fixed-delay
任务保证同一个任务多次执行之间间隔固定时间,fixed-rate
任务保证任务执行按照固定的频率执行,其中任务类型使用period
的值来区分
3、成员变量
public class ScheduledThreadPoolExecutor extends ThreadPoolExecutor implements ScheduledExecutorService {
......
}
关于 ThreadPoolExecutor 具体看:线程池:ThreadPoolExecutor 源码解析
关于 ScheduledExecutorService 具体看:Executor框架底层:Executor、ExecutorService、AbstractExecutorService 和 ScheduledExecutorService
//shutdown后是否继续执行定时任务
private volatile boolean continueExistingPeriodicTasksAfterShutdown;
//shutdown后是否继续执行延迟任务
private volatile boolean executeExistingDelayedTasksAfterShutdown = true;
//cancle方法需要将该任务从队列移除
private volatile boolean removeOnCancel = false;
//任务的序列号
private static final AtomicLong sequencer = new AtomicLong();
4、重要内部类
4.1、ScheduledFutureTask
4.1.1、介绍
在ScheduledThreadPoolExecutor中,所有的任务都会被封装成ScheduledFutureTask,无论是runnable还是callable,无论是否需要延迟和定时。
private class ScheduledFutureTask<V> extends FutureTask<V> implements RunnableScheduledFuture<V> {
......
}
关于 FutureTask 的详细介绍:线程池系列 之 FutureTask 及其底层实现
4.1.2、成员变量
//序列号,CAS自动递增(在构造方法里面)
private final long sequenceNumber;
//周期任务的下一次执行时间
private long time;
//定时周期
private final long period;
//实际任务
RunnableScheduledFuture<V> outerTask = this;
//任务在队列中的下标
int heapIndex;
4.1.3、构造方法
ScheduledFutureTask(Callable<V> callable, long ns) {
super(callable);
this.time = ns;
this.period = 0;
this.sequenceNumber = sequencer.getAndIncrement(); // 序号递增
}
ScheduledFutureTask(Runnable r, V result, long ns) {
super(r, result);
this.time = ns;
this.period = 0;
this.sequenceNumber = sequencer.getAndIncrement();
}
ScheduledFutureTask(Runnable r, V result, long ns, long period) {
super(r, result);
this.time = ns;
this.period = period;
this.sequenceNumber = sequencer.getAndIncrement();
}
4.1.4、run()
ScheduledFutureTask由任务封装而来,执行任务就是执行ScheduledFutureTask#run()方法,ScheduledThreadPoolExecutor的周期执行的功能也是在run方法中实现
// 重写FutureTask,如果是周期性任务需要重新放入队列
public void run() {
boolean periodic = isPeriodic(); //任务是否需要周期执行
if (!canRunInCurrentRunState(periodic)) // 检查当前状态 不能执行任务,则取消任务
cancel(false);
else if (!periodic) //如果不是周期任务,调用FutureTask.run()执行任务(非周期任务直接执行)
ScheduledFutureTask.super.run();
// 到这里说明是:周期性任务
else if (ScheduledFutureTask.super.runAndReset()) { //周期任务的执行
//FutureTask的runAndReset方法
// 与run方法的不同就是正常完成后任务的状态不会变化,依旧是NEW,且返回值为成功或失败,不会设置result属性
setNextRunTime(); //设置任务下次执行时间
reExecutePeriodic(outerTask);
}
}
关于 FutureTask.run() 具体见:线程池系列 之 FutureTask 及其底层实现
isPeriodic()
判断任务是否周期执行
public boolean isPeriodic() {
return period != 0;
}
canRunInCurrentRunState(boolean periodic) (ScheduledThreadPoolExecutor的成员方法)
检查当前状态能否执行任务;线程池处于RUNNING则必然可以执行,SHUTDOWN状态则需要检查对应的参数设置
boolean canRunInCurrentRunState(boolean periodic) {
return isRunningOrShutdown(periodic ?
continueExistingPeriodicTasksAfterShutdown : // shutdown后是否继续执行定时任务
executeExistingDelayedTasksAfterShutdown); // shutdown后是否继续执行延迟任务
}
// isRunningOrShutdown是ThreadPoolExecutor中的方法
// 作用是:判断是否是 rujning或者shutdown状态;如果是,返回true
final boolean isRunningOrShutdown(boolean shutdownOK) {
int rs = runStateOf(ctl.get());
return rs == RUNNING || (rs == SHUTDOWN && shutdownOK);
}
4.1.5、cancel(boolean mayInterruptIfRunning)
执行任务的取消逻辑(FutureTask的cancel方法),然后根据removeOnCancel参数设置确定是否需要从队列移除
public boolean cancel(boolean mayInterruptIfRunning) {
// 调用FutureTask的cancel方法
boolean cancelled = super.cancel(mayInterruptIfRunning);
// 如果取消成功,可以移除,并且任务在队列中的下标>=0;则从队列中移除任务
if (cancelled && removeOnCancel && heapIndex >= 0)
remove(this); // ThreadPoolExecutor.remove()
return cancelled;
}
关于 ThreadPoolExecutor.remove() 见:线程池:ThreadPoolExecutor 源码解析
4.1.6、setNextRunTime()
private void setNextRunTime() {
long p = period; //取出周期时间
if (p > 0)
time += p; //time是周期任务的下一次执行时间
else
time = triggerTime(-p);
}
// ScheduledThreadPoolExecutor中的方法
long triggerTime(long delay) {
return now() + ((delay < (Long.MAX_VALUE >> 1)) ? delay : overflowFree(delay));
}
private long overflowFree(long delay) {
Delayed head = (Delayed) super.getQueue().peek();
if (head != null) {
long headDelay = head.getDelay(NANOSECONDS);
if (headDelay < 0 && (delay - headDelay < 0))
delay = Long.MAX_VALUE + headDelay;
}
return delay;
}
这儿可以看到,周期时间period有正有负,这是ScheduledThreadPoolExecutor的ScheduledAtFixedRate和ScheduledWithFixedDelay的方法区别,前者为正数,后者为负数。
正数时,下一次执行时间为原来的执行时间+周期,即以执行开始时间为基准。
负数时,不考虑溢出情况,下一次执行时间为当前时间+周期,即以执行结束时间为基准。
如果溢出,下一次执行时间为Long.MAX_VALUE + headDelay。
为什么要考虑溢出?
因为在任务加入队列时要将该任务与堆中的部分原有任务在siftUp中通过compareTo进行比较,compareTo比较的是time的差值,如果出现溢出,会导致队列排序不正常,任务的执行混乱的结果。
为什么只考虑向上溢出,不需要考虑向下溢出
因为compareTo中比较的是time的差值,time都是long类型的正数,两个long类型的正数相减显然不会出现向下溢出。
溢出的判断条件是什么?
compareTo中比较的是time的差值,相当于两个节点getDelay的差值。差值最大的必然的该任务与队列头结点任务的差值。因此要判断会不会出现溢出,只需要判断delay - headDelay会不会溢出,delay必然是正数,要出现溢出则headDelay必为负数(也就是任务可以出队列但没有出队列,任务没有及时得到执行)的情况,因此判断条件就是:headDelay < 0 且 (delay - headDelay > Long.MAX_VALUE),即headDelay < 0 && (delay - headDelay < 0)。
为什么delay < (Long.MAX_VALUE >> 1)时不需要考虑溢出?
没想明白
为什么period为正数时不需要考虑溢出?
因为conpareTo比较的是time的差,假设当前任务的time为A,当前队列头结点任务的time为B,因为A比B先出队列,所以A < B。显然A + period - B < period < Long.MAX_VALUE,不会出现溢出的情况。
4.1.7、reExecutePeriodic(RunnableScheduledFuture<?> task)
任务的下一次执行安排
如果当前线程池状态可以执行周期任务,加入队列,并开启新线程。
void reExecutePeriodic(RunnableScheduledFuture<?> task) {
if (canRunInCurrentRunState(true)) { // 检查当前状态 能执行周期任务
// ThreadPoolExecutor.workQueue.add(task)
super.getQueue().add(task); //将task添加到线程池workQueue中
// 再次检查当前状态
// 如果当前状态不能执行周期任务,将task从队列中移除;如果移除成功,则取消task
if (!canRunInCurrentRunState(true) && remove(task))
task.cancel(false);
// 如果当前状态能执行周期任务
// 或者不能执行周期任务,但是从队列中移除task失败,则确保至少一个线程在处理任务
else
ensurePrestart(); // ThreadPoolExecutor
}
}
task.cancel(false) 见:线程池系列 之 FutureTask 及其底层实现
4.1.8、compareTo(Delayed other)
compareTo 作用是加入元素到延迟队列后,内部建立或者调整堆时候会使用该元素的 compareTo 方法与队列里面其他元素进行比较,让最快要过期的元素放到队首。所以无论什么时候向队列里面添加元素,队首的的元素都是最即将过期的元素。如果时间相同,序列号小的排前面。
ScheduledFutureTask需要放入DelayedWorkQueue,而DelayedWorkQueue是从小到大进行排序的。
在ScheduledFutureTask,其大小的排序根据执行时间time正序排列,如果相同,在按照序列号sequenceNumber 正序排列。
public int compareTo(Delayed other) {
// 如果2个指向的同一个对象,则返回0
if (other == this)
return 0;
// other必须是ScheduledFutureTask类型的
if (other instanceof ScheduledFutureTask) {
ScheduledFutureTask<?> x = (ScheduledFutureTask<?>)other;
long diff = time - x.time; //当前执行时间(堆顶的task)-x.time
// 如果堆顶task 比 x更快过期,则返回-1
if (diff < 0)
return -1;
// 如果x 比 堆顶task更快过期,则返回1
else if (diff > 0)
return 1;
// 到这里,说明她两的过期时间相同
// 比较序列号
else if (sequenceNumber < x.sequenceNumber) // 序列号,CAS自动递增
return -1;
else
return 1;
}
// 到这里,说明 other不是ScheduledFutureTask类型的
long diff = getDelay(NANOSECONDS) - other.getDelay(NANOSECONDS);
return (diff < 0) ? -1 : (diff > 0) ? 1 : 0;
}
getDelay(TimeUnit unit)
用来获取当前任务还有多少时间就过期了
// 元素过期算法,装饰后时间-当前时间,就是即将过期剩余时间
public long getDelay(TimeUnit unit) {
return unit.convert(time - now(), NANOSECONDS);
}
4.2、DelayedWorkQueue
4.2.1、简介
static class DelayedWorkQueue extends AbstractQueue<Runnable> implements BlockingQueue<Runnable> {
......
}
关于 BlockingQueue 详细介绍:JDK8系列:刨根问底 之 队列
DelayedWorkQueue是BlockingQueue的实现类,是专门为了ScheduledThreadPoolExecutor设计的队列,队列中的数据结构为最小堆,每个节点的值小于其子节点的值。初始容量为16,节点数量大于数组长度后触发扩容,容量变为原来的1.5倍。
数据结构:小顶堆,按照到期时间排序,最早过期的task在堆顶
每次删除元素时,将最后一个元素放在堆顶,然后向下遍历,调整顺序(实际代码没有将最后元素放在堆顶这一步)
每次插入元素时,比较插入元素和父节点的大小,向上遍历调整位置
take阻塞操作
如果 queue==null,则需要等待;
如果queue!=null,但是堆顶task时间未到,还是要阻塞等待(可能还要调整leader);
如果queue!=null,并且task时间到了,直接返回堆顶task
4.2.2、成员变量
private static final int INITIAL_CAPACITY = 16; // 初始容量
// 存放节点
private RunnableScheduledFuture<?>[] queue = new RunnableScheduledFuture<?>[INITIAL_CAPACITY];
private int size = 0; // 节点数量
// 控制并发和阻塞等待
private final ReentrantLock lock = new ReentrantLock();
private final Condition available = lock.newCondition();
// 正在阻塞等待头结点的线程
private Thread leader = null;
leader属性用于表示正在阻塞等待头结点的线程,是一种Leader-Follower模式的变种,可以最小化线程的等待时间。同样通过阻塞方式去获取头结点,那么leader线程的等待时间为头结点的延迟时间,其它线程则会陷入阻塞状态(available.await())。leader线程获取到头结点后需要发送信号唤醒其它线程(available.asignAll())。
4.2.3、插入节点
以DelayedWorkQueue#offer(Runnable)为例,其它插入节点的方法都是通过调用该方法完成
public boolean offer(Runnable x) {
//空值校验
if (x == null)
throw new NullPointerException();
RunnableScheduledFuture<?> e = (RunnableScheduledFuture<?>)x; //强转类型
final ReentrantLock lock = this.lock;
lock.lock(); //加锁
try {
int i = size;
// 超过容量,扩容
if (i >= queue.length)
grow();
size = i + 1; //更新当前节点数
//插入的是第一个节点(阻塞队列原本为空)
if (i == 0) {
queue[0] = e;
setIndex(e, 0); //setIndex(e, 0)用于修改ScheduledFutureTask的heapIndex属性,表示该对象在队列里的下标
} else { //阻塞队列非空
siftUp(i, e); //在插入新节点后对堆进行调整,进行节点上移,保持其特性(节点的值小于子节点的值)不变
}
//插入的是头结点,不需要leader,唤醒等待头结点的阻塞线程
// 因为leader表示正在阻塞等待头结点的线程,而当前没有线程在等待
if (queue[0] == e) {
leader = null;
available.signal();
}
} finally {
lock.unlock(); //解锁
}
return true;
}
(1)setIndex(RunnableScheduledFuture<?> f, int idx)
setIndex(e, 0)用于修改ScheduledFutureTask的heapIndex属性,表示该对象在队列里的下标
private void setIndex(RunnableScheduledFuture<?> f, int idx) {
if (f instanceof ScheduledFutureTask)
((ScheduledFutureTask)f).heapIndex = idx;
}
(2)siftUp(int k, RunnableScheduledFuture<?> key)
用于在插入新节点后对堆进行调整,进行节点上移,保持其特性(节点的值小于子节点的值)不变。
从插入的节点开始循环比较,节点小于父节点,则上移。
private void siftUp(int k, RunnableScheduledFuture<?> key) {
while (k > 0) {
int parent = (k - 1) >>> 1; //父节点坐标
RunnableScheduledFuture<?> e = queue[parent]; //获取父节点的值
// 如果 节点>= 父节点,确定最终位置
if (key.compareTo(e) >= 0)
break;
// 节点<父节点,将节点向上移动(就是将父节点放在k处)
queue[k] = e;
setIndex(e, k);
k = parent; //更新父节点
}
// 确定key的最后落脚处
queue[k] = key;
setIndex(key, k);
}
(3)grow() 扩容
表示扩容,数组长度修改为原来的1.5倍后复制数组中的元素。
private void grow() {
int oldCapacity = queue.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); //新容量为原来的1.5倍
if (newCapacity < 0)
newCapacity = Integer.MAX_VALUE;
queue = Arrays.copyOf(queue, newCapacity); //从旧数组 复制到 新数组
}
4.2.4、删除节点(不一定是队列头节点)
DelayedWorkQueue#remove(Runnable)为例,其它删除方法也是通过调用盖方法完成。
public boolean remove(Object x) {
final ReentrantLock lock = this.lock;
lock.lock(); //加锁
try {
int i = indexOf(x); //定位x
//节点元素不存在
if (i < 0)
return false;
//修改元素的heapIndex
setIndex(queue[i], -1);
int s = --size;
//末节点作为替代节点
RunnableScheduledFuture<?> replacement = queue[s];
queue[s] = null; //原本末节点处置空,便于GC
if (s != i) {
//下移,保证该节点的子孙节点保持特性
siftDown(i, replacement);
//上移,保证该节点的祖先节点保持特性
//上移和下移不可能同时发生(!!!)
// queue[i] == replacement说明下移没有发生
if (queue[i] == replacement)
siftUp(i, replacement);
}
return true;
} finally {
lock.unlock(); //加锁
}
}
(1)indexOf(Object x)
查找对象在队列中的下标
private int indexOf(Object x) {
if (x != null) {
if (x instanceof ScheduledFutureTask) {
int i = ((ScheduledFutureTask) x).heapIndex;
// Sanity check; x could conceivably be a
// ScheduledFutureTask from some other pool.
if (i >= 0 && i < size && queue[i] == x)
return i;
} else {
for (int i = 0; i < size; i++)
if (x.equals(queue[i]))
return i;
}
}
return -1;
}
(2)siftDown(int k, RunnableScheduledFuture<?> key)
循环,选择当前节点的子节点中较小的节点与当前节点进行比较,如果当前节点小于子节点,则下移
private void siftDown(int k, RunnableScheduledFuture<?> key) {
int half = size >>> 1;
while (k < half) {
int child = (k << 1) + 1; //左子节点坐标
RunnableScheduledFuture<?> c = queue[child]; //c表示左右子节点中的较小者,暂时是左
int right = child + 1; //右子节点坐标
if (right < size && c.compareTo(queue[right]) > 0)
c = queue[child = right];
// 当前按节点 <= 子节点中的较小者,找到最终位置
if (key.compareTo(c) <= 0)
break;
// 否则,当前节点下移
queue[k] = c;
setIndex(c, k);
k = child;
}
queue[k] = key;
setIndex(key, k);
}
4.2.5、非阻塞取出头结点poll
DelayedWorkQueue#poll()
// 其实就是调用了 finishPoll
public RunnableScheduledFuture<?> poll() {
final ReentrantLock lock = this.lock;
lock.lock(); //加锁
try {
RunnableScheduledFuture<?> first = queue[0];
if (first == null || first.getDelay(NANOSECONDS) > 0)
return null;
else
return finishPoll(first);
} finally {
lock.unlock(); //释放锁
}
}
可以看到
(1)队列中没有元素:返回null。
(2)头结点的延迟时间没到:返回null。(区别于原来的阻塞队列)
(3)头结点达到延迟时间:末节点作为替代节点下移调整堆结构后返回头结点。
finishPoll(RunnableScheduledFuture<?> f)
// f是队列头节点(!!!)
private RunnableScheduledFuture<?> finishPoll(RunnableScheduledFuture<?> f) {
int s = --size;
RunnableScheduledFuture<?> x = queue[s]; //取出队列尾节点的值(之后放到合适位置)
queue[s] = null; //置空,便于GC
// 尾节点从0开始向下遍历调整顺序
if (s != 0)
siftDown(0, x);
setIndex(f, -1); //设置f的heapIndex属性
return f;
}
4.2.6、阻塞获取头结点
以DelayedWorkQueue#take()为例,DelayedWorkQueue#poll(long, TimeUnit)类似,ScheduledThreadPoolExecutor的延迟执行功能就是通过阻塞获取头结点实现的
public RunnableScheduledFuture<?> take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly(); //加锁,响应中断
try {
// 死循环
for (;;) {
RunnableScheduledFuture<?> first = queue[0]; //头节点
// 如果队列为null,则等待在条件上
if (first == null)
available.await();
// 如果队列非空,还得判断 延时时间是否满足条件
else {
long delay = first.getDelay(NANOSECONDS);
// 如果时间到了,取出头节点
if (delay <= 0)
return finishPoll(first);
// 头节点时间没到,还不能取出头节点
first = null; // 等待的时候,不要持有头节点
//已经存在leader线程,当前线程await阻塞
if (leader != null)
available.await();
//如果不存在leader线程,当前线程作为leader线程,并制定头结点的延迟时间作为阻塞时间
else {
Thread thisThread = Thread.currentThread();
leader = thisThread;
try {
available.awaitNanos(delay);
} finally {
//leader线程阻塞结束
if (leader == thisThread)
leader = null;
}
}
}
}
} finally {
//leader线程没有阻塞,可以找到头结点,唤醒阻塞线程
if (leader == null && queue[0] != null)
available.signal();
lock.unlock();
}
}
5、构造方法
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue());
}
public ScheduledThreadPoolExecutor(int corePoolSize, RejectedExecutionHandler handler) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue(), handler);
}
public ScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue(), threadFactory);
}
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue(), threadFactory, handler);
}
6、ScheduledThreadPoolExecutor执行任务
在对DelayedWorkQueue和ScheduledFutureTask这两个内部类进行解析后,正式针对ScheduledThreadPoolExecutor的方法进行解析。
6.1、ScheduledThreadPoolExecutor#schedule
(1)延迟执行callable任务
public <V> ScheduledFuture<V> schedule(Callable<V> callable,
long delay,
TimeUnit unit) {
// 参数异常
if (callable == null || unit == null)
throw new NullPointerException();
// 将当前任务封装成 ScheduledFutureTask
RunnableScheduledFuture<V> t = decorateTask(callable,
new ScheduledFutureTask<V>(callable,triggerTime(delay, unit)));
// 调用delayedExecute执行任务
delayedExecute(t);
return t;
}
(2)延迟执行runnable任务
public ScheduledFuture<?> schedule(Runnable command,
long delay,
TimeUnit unit) {
// 参数异常
if (command == null || unit == null)
throw new NullPointerException();
// 封装任务 --> RunnableScheduledFuture
RunnableScheduledFuture<?> t = decorateTask(command,
new ScheduledFutureTask<Void>(command, null, triggerTime(delay, unit)));
delayedExecute(t); //执行任务
return t;
}
6.2、 scheduleAtFixedRate
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();
// 将task --> ScheduledFutureTask(带周期)
ScheduledFutureTask<Void> sft = new ScheduledFutureTask<Void>(command,
null,
triggerTime(initialDelay, unit),
unit.toNanos(period));
RunnableScheduledFuture<Void> t = decorateTask(command, sft); //直接返回,没有任何操作,保留给子类扩展
sft.outerTask = t; // 实际任务(sft和t一摸一样)
delayedExecute(t); //执行任务
return t;
}
延迟且周期执行,到点执行,开始执行时间 = 上一次的执行开始时间 + period。但是任务执行完成之后,下一次任务才能加入队列。所以准确得说,开始执行时间 = max(上一次执行开始时间 + period, 上一次执行结束时间)
ScheduledFutureTask#decorateTask(Runnable, RunnableFutureTask)
没有做任何操作,直接将task返回。
该方法主要目的是用于子类扩展。
protected <V> RunnableScheduledFuture<V> decorateTask(
Runnable runnable, RunnableScheduledFuture<V> task) {
return task;
}
6.3、ScheduledThreadPoolExecutor#scheduleWithFixedDelay
延迟且周期执行,到点执行,固定的延迟时间,开始执行时间 = 上一次任务执行结束时间 + delay。
与scheduleAtFixedRate的区别是通过传入ScheduledFutureTask的period的正负情况确定的,具体实现方式已经在上文ScheduledFutureTask的分析中提到。
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();
// 封装任务 task --> 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;
}
6.4、ScheduledFutureTask#delayedExecute(RunnableScheduledFuture)
校验状态,提交任务,调用ensurePrestart()方法开启线程
private void delayedExecute(RunnableScheduledFuture<?> task) {
// 如果线程池关闭了,拒绝任务
if (isShutdown())
reject(task);
// 线程池没有关闭
else {
super.getQueue().add(task); // 任务加入workQueue队列
//再次检查,线程池状态发生变化,不能够执行了,移出队列,取消任务
if (isShutdown() &&
!canRunInCurrentRunState(task.isPeriodic()) &&
remove(task))
task.cancel(false);
else
ensurePrestart(); //确保至少一个线程在处理任务
// workQueue有task,工作线程非空,它会自动到队列中去任务执行
}
}
7、关闭 线程池
ScheduledFutureTask#onShutdown()
线程池状态由RUNNING >> SHUTDOWN变化时的钩子函数。
keepDelayed :shutdown后是否继续执行延迟任务
keepPeriodic :shutdown后是否继续执行周期任务
将不需要执行的延迟任务和周期任务进行取消并从队列中移除。
最后在检查tryTerminate()方法。
void onShutdown() {
BlockingQueue<Runnable> q = super.getQueue(); //获得workQueue
// keepDelayed :shutdown后是否继续执行延迟任务
boolean keepDelayed = getExecuteExistingDelayedTasksAfterShutdownPolicy();
// keepPeriodic :shutdown后是否继续执行周期任务
boolean keepPeriodic = getContinueExistingPeriodicTasksAfterShutdownPolicy();
// 如果shutdown之后,延迟任务 和 周期任务都不需要执行;则遍历workQueue,将全部任务取消,同时清空workQueue
if (!keepDelayed && !keepPeriodic) {
for (Object e : q.toArray())
if (e instanceof RunnableScheduledFuture<?>)
((RunnableScheduledFuture<?>) e).cancel(false);
q.clear();
}
//
else {
for (Object e : q.toArray()) {
if (e instanceof RunnableScheduledFuture) {
// 封装 task --> RunnableScheduledFuture
RunnableScheduledFuture<?> t = (RunnableScheduledFuture<?>)e;
// 满足情况,则取消任务,从workQueue中删除task
if ((t.isPeriodic() ? !keepPeriodic : !keepDelayed) ||
t.isCancelled()) {
if (q.remove(t))
t.cancel(false);
}
}
}
}
tryTerminate();
}
8、提交任务 submit
就是调用的 schedule() 方法
submit(Callable<T> task)
public <T> Future<T> submit(Callable<T> task) {
return schedule(task, 0, NANOSECONDS);
}
submit(Runnable task)
public Future<?> submit(Runnable task) {
return schedule(task, 0, NANOSECONDS);
}
public <T> Future<T> submit(Runnable task, T result) {
return schedule(Executors.callable(task, result), 0, NANOSECONDS);
}
参考资料:
线程池ScheduledThreadPoolExecutor源码解析