目录
4个字段,方法now,内部类ScheduledTutureTask
方法canRunInCurrentRunState,delayedExecute,reExecutePeriodic,onShutdown,2个decorateTask,4个构造函数
方法2个triggerTime,overflowFree,4个scheduleXXX,execute,3个submit
方法3个setXXX/getXXX,shutdown,shutdownNow,getQueue
简介
package java.util.concurrent;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.*;
/**
* 一个ThreadPoolExecutor,它可以额外调度命令在给定的延迟后运行,或定期执行。
* 当需要多个工作线程时,或者当需要ThreadPoolExecutor(这个类的扩展)的额外灵活性或功能时,这个类比java.util. Timer更可取。
*
* <p>延迟任务的执行不会早于它们被启用的时间,但是对于它们被启用后何时开始执行没有任何实时的保证。
* 计划完全相同执行时间的任务以先进先出(FIFO)的提交顺序启用。
*
* <p>当提交的任务在运行之前被取消时,执行将被抑制。
* 默认情况下,这样一个被取消的任务不会自动从工作队列中删除,直到它的延迟时间结束。
* 虽然这可以进行进一步的检查和监视,但它也可能导致取消任务的无限保留。
* 为了避免这种情况,将setRemoveOnCancelPolicy设置为true,这将导致任务在取消时立即从工作队列中删除。
*
* <p>通过scheduleAtFixedRate或scheduleWithFixedDelay调度的任务的连续执行不会重叠。
* 虽然不同的执行可能由不同的线程执行,但前一次执行的效果发生在后续执行的效果之前。
*
* <p>虽然这个类继承自ThreadPoolExecutor,但继承的一些调优方法对它没有用处。
* 特别是,因为它使用corePoolSize线程和无界队列作为固定大小的池,所以调整maximumPoolSize没有任何有用的效果。
* 此外,将corePoolSize设置为0或使用allowCoreThreadTimeOut几乎从来不是一个好主意,
* 因为这可能会使线程池没有线程来处理任务,一旦他们成为合格的运行者。
*
* <p>扩展注释:
* 这个类重写execute和submit方法来生成内部ScheduledFuture对象来控制每个任务的延迟和调度。
* 为了保留功能,子类中对这些方法的任何进一步覆盖都必须调用超类版本,这有效地禁用了额外的任务定制。
* 但是,这个类提供了另一个受保护的扩展方法decorateTask (Runnable和Callable各有一个版本),可以用来定制具体的任务类型,
* 用于执行通过execute、submit、schedule、scheduleAtFixedRate和scheduleWithFixedDelay输入的命令。
* 默认情况下,ScheduledThreadPoolExecutor使用扩展FutureTask的任务类型。但是,可以使用子类的形式来修改或替换:
*
* <pre> {@code
* public class CustomScheduledExecutor extends ScheduledThreadPoolExecutor {
*
* static class CustomTask<V> implements RunnableScheduledFuture<V> { ... }
*
* protected <V> RunnableScheduledFuture<V> decorateTask(
* Runnable r, RunnableScheduledFuture<V> task) {
* return new CustomTask<V>(r, task);
* }
*
* protected <V> RunnableScheduledFuture<V> decorateTask(
* Callable<V> c, RunnableScheduledFuture<V> task) {
* return new CustomTask<V>(c, task);
* }
* // ... add constructors, etc.
* }}</pre>
*
* ThreadPoolExecutor的功能已经很强大了,但却没有延时任务的功能。
* 对于ScheduledThreadPoolExecutor,它的父类ThreadPoolExecutor所做的提交任务都是延时时间为0的延时任务。
* 同时,它也提供了执行周期任务的功能。
*
* 从类定义可知,ScheduledThreadPoolExecutor继承了ThreadPoolExecutor,
* 实际上它也是复用了ThreadPoolExecutor的大部分逻辑,只是加了自己的特化处理。
*
* 有两种周期任务:FixedRate和FixedDelay。
* 下一个周期的重复任务能够进入队列的前提是,当前周期执行重复任务时没有抛出异常。
* FixedRate是当前周期的 开始时刻 与下一个周期的开始时刻总是相差period单位时间。但如果当前周期执行花费太多时间,会导致下一个周期迟迟无法入队。
* FixedDelay是当前周期的 结束时刻 与下一个周期的开始时刻总是相差period单位时间。当前周期的执行时间有多久都不影响。
* 任务入队后,即将当前时刻已经可以被执行,如果没有空闲的工作线程来取task,那么也不会被执行。因为ScheduledThreadPoolExecutor最多只能起corePoolSize个线程。
* 关于shutdown的逻辑有点不一样,它是根据两个run-after-shutdown参数来判断队列剩余任务在shutdown后是否可以继续被执行。
* @since 1.5
* @author Doug Lea
*/
public class ScheduledThreadPoolExecutor
extends ThreadPoolExecutor
implements ScheduledExecutorService
4个字段,方法now,内部类ScheduledTutureTask
/*
* 这个类专门用于ThreadPoolExecutor的实现
*
* 1. 使用自定义任务类型,ScheduledFutureTask的任务,甚至那些不需要调度的任务
* (即,那些使用ExecutorService执行而不是ScheduledExecutorService方法提交的任务),这些任务被视为延迟为0的任务。
*
* 2. 使用自定义队列(DelayedWorkQueue),无界DelayQueue的变体。
* 与ThreadPoolExecutor相比,缺乏容量约束以及corePoolSize和maximumPoolSize实际上是相同的这一事实,简化了某些执行机制(参见delayedExecute)。
*
* 3. 支持可选的run-after-shutdown参数,这将导致覆盖shutdown方法来删除和取消关机后不应该运行的任务,
* 以及当任务(重新)提交与shutdown重叠时不同的重新检查逻辑。
*
* 4. 任务装饰方法允许拦截和插装,这是必需的,因为子类不能重写提交方法来获得这种效果。
* 但是这些对池控制逻辑没有任何影响。
*/
/**
* 如果在关机时取消/抑制定期任务,则为False。
* 线程池状态变成Shutdown后,是否还需要执行队列中的周期任务
*/
private volatile boolean continueExistingPeriodicTasksAfterShutdown;
/**
* 如果在关机时取消非周期性任务,则为False。
* 线程池状态变成Shutdown后,是否还需要执行队列中的延迟任务。默认为true
*/
private volatile boolean executeExistingDelayedTasksAfterShutdown = true;
/**
* 如果ScheduledFutureTask.Cancel应该从队列中删除,则为true
* 取消Future后,是否将其从队列中移除
*/
private volatile boolean removeOnCancel = false;
/**
* 序号打破调度关系,并反过来保证在捆绑条目之间的先进先出顺序。
* 如果两个任务的到期时间相同,则通过这个比较前后
*/
private static final AtomicLong sequencer = new AtomicLong();
/**
* 返回当前纳秒时间。
*/
final long now() {
return System.nanoTime();
}
// ScheduledFutureTask实例在delayedExecute中入队到task队列中,所以工作线程出队task后执行的是ScheduledFutureTask#run()。
// 这个类图看起来很吓人,其实我们只需要关注它同时实现了Runnable Future Delayed接口即可,
// 当然,ScheduledFutureTask的逻辑大量复用了其父类FutureTask的逻辑。
private class ScheduledFutureTask<V>
extends FutureTask<V> implements RunnableScheduledFuture<V> {
/** 打破FIFO顺序编号 通过原子类获得的独一无二的序号,当两个task的time相同时,用这个属性来决定先后*/
private final long sequenceNumber;
/** 以nanoTime为单位启用任务执行的时间 一个绝对时间,从这个时间往后,当前task可以被执行*/
private long time;
/**
* 周期值。
* 1. 正数代表是fixed-rate周期任务。
* 2. 负数代表是fixed-delay周期任务。
* 3. 0代表是延时任务。
*/
private final long period;
/** 下一个周期将要被执行的task,默认是this */
RunnableScheduledFuture<V> outerTask = this;
/**
* task在堆上的索引,标识作用,当被从堆中删除时,索引为-1,正常为非负数。用来判断是否需要remove
*/
int heapIndex;
/**
* 使用给定的基于nanotime的触发器时间创建一次性操作。
*/
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();
}
/**
* 使用给定的基于nanotime的触发器时间创建一次性操作。
*/
ScheduledFutureTask(Callable<V> callable, long ns) {
super(callable);
this.time = ns;
this.period = 0;
this.sequenceNumber = sequencer.getAndIncrement();
}
public long getDelay(TimeUnit unit) {
return unit.convert(time - now(), NANOSECONDS);
}
public int compareTo(Delayed other) {
if (other == this) // compare zero if same object
return 0;
if (other instanceof ScheduledFutureTask) {
ScheduledFutureTask<?> x = (ScheduledFutureTask<?>)other;
long diff = time - x.time;
if (diff < 0)
return -1;
else if (diff > 0)
return 1;
else if (sequenceNumber < x.sequenceNumber)
return -1;
else
return 1;
}
long diff = getDelay(NANOSECONDS) - other.getDelay(NANOSECONDS);
return (diff < 0) ? -1 : (diff > 0) ? 1 : 0;
}
/**
* 如果这是一个周期操作(而不是一次性操作)返回true。
*
* @return {@code true} if periodic
*/
public boolean isPeriodic() {
return period != 0;
}
/**
* 设置周期性任务的下一次运行时间。
*/
private void setNextRunTime() {
long p = period;
if (p > 0) //固定速率执行,scheduleAtFixedRate
time += p; //按照上一次周期的开始时刻,再加上周期值
else //固定延迟执行,scheduleWithFixedDelay
time = triggerTime(-p); //按照现在时刻(即当前周期的结束时刻),再加上周期值
}
//ScheduledFutureTask内部类
public boolean cancel(boolean mayInterruptIfRunning) {
// super.cancel(mayInterruptIfRunning)这里是直接调用到ScheduledFutureTask的父类FutureTask的实现。
// 而出队成功则说明这个task肯定没有被工作线程执行过task.run(),所以上面的super.cancel(mayInterruptIfRunning)
// 肯定会使得FutureTask的状态变成CANCELLED或INTERRUPTED。
// 即使取消成功,还需要判断&& removeOnCancel && heapIndex >= 0,removeOnCancel代表取消后是否需要删除,
// 而heapIndex则是task在最小堆中的索引,如果它存在于堆中那么索引是>= 0的。
// remove(this)看起来有点重复操作,这其实因为ScheduledFutureTask#cancel会在ScheduledFutureTask#run中调用。
// 考虑本章的执行过程,这里会heapIndex不满足而不会执行remove(this)。
boolean cancelled = super.cancel(mayInterruptIfRunning);//直接调用到父类FutureTask的实现
if (cancelled && removeOnCancel && heapIndex >= 0)
remove(this);//调用到外部类的方法,也就是上面的方法
return cancelled;
}
/**
* 重写FutureTask版本,以便在周期性时重置/requeue。
*
* 上面说过,scheduleAtFixedRate在一定条件下,当前周期的开始时刻与下一个周期的开始时刻总是相差period单位时间。
* 虽然setNextRunTime确实能保证,两个周期的开始时刻总是相差period,
* 但加入队列的时机是ScheduledFutureTask.super.runAndReset()完成后,如果task执行时间需要很久已经超过了period,
* 那么下一个周期的实际开始时刻的差距则肯定超过了period了,即这种情况下,下一个周期的任务加入队列时,就已经是可以被执行的状态了。
*
* 而scheduleWithFixedDelay的逻辑,则完全符合ScheduledFutureTask.super.runAndReset()的完成时机,
* runAndReset执行完后setNextRunTime会以当前时刻(当前周期的结束时刻)再加上period作为新任务的time。
* 而在reExecutePeriodic执行后,即使这个task马上就能被执行,如果没有空闲的工作线程,那么这个task也得等到有空闲线程时,才会被执行。
*/
public void run() {
boolean periodic = isPeriodic();
if (!canRunInCurrentRunState(periodic))//根据当前线程池状态,是否可以运行
cancel(false);//取消task,并根据removeOnCancel移除task
//如果可以运行
else if (!periodic)//如果不是周期任务,那就执行父类run
ScheduledFutureTask.super.run();
//如果是周期任务,那就执行父类的runAndReset。
// 相比run函数,这个函数有些不同:
// 正常执行完c.call()后,不会去执行set()。因为本来c.call()就没有返回值。
// runAndReset具有返回值,ran && s == NEW前者代表c.call()没有抛出异常
//(如果执行了setException,状态也会经历NEW -> COMPLETING -> EXCEPTIONAL的),
// 后者代表没有被执行过task.cancel()(可能经历NEW -> CANCELLED或NEW -> INTERRUPTING -> INTERRUPTED)。
// 总之,没有出现上面的异常情况的话,task状态会一直保持NEW的。
// 如果ScheduledFutureTask.super.runAndReset()没有发现异常情况,并顺利返回true的话,那么就把下一个周期的重复task入队:
else if (ScheduledFutureTask.super.runAndReset()) {//返回true才会把下一个重复task放入队列
setNextRunTime();
// reExecutePeriodic将重复task入队,整个过程和delayedExecute类似。
reExecutePeriodic(outerTask);
}
}
}
方法canRunInCurrentRunState,delayedExecute,reExecutePeriodic,onShutdown,2个decorateTask,4个构造函数
/**
* 如果可以运行给定当前运行状态和运行后关闭参数的任务,则返回true。
*
*
* @param periodic true if this task periodic, false if delayed
*/
boolean canRunInCurrentRunState(boolean periodic) {
// 如果是RUNNING状态,那肯定可以执行阿。返回true。
// 如果是SHUTDOWN状态,则根据传入参数决定。返回shutdownOK的值。
// 如果是SHUTDOWN之后的值(STOP、TIDYING、TERMINATED),那肯定不可以执行。返回false。
// 总之,canRunInCurrentRunState返回false,就代表刚入队的task不能被执行。
return isRunningOrShutdown(periodic ?
continueExistingPeriodicTasksAfterShutdown :
executeExistingDelayedTasksAfterShutdown);
}
/**
* 延迟或周期性任务的主要执行方法。如果池关闭,拒绝任务。
* 否则,将任务添加到队列中,并在必要时启动一个线程来运行它。
* (我们不能预先启动线程来运行该任务,因为该任务(可能)还不应该运行。)
* 如果在添加任务时池已关闭,则根据状态和关机后运行参数的要求取消并删除它。
*
* 所有提交任务的函数都调用到了delayedExecute,它才是提交任务的关键逻辑。
*
* @param task the task
*/
private void delayedExecute(RunnableScheduledFuture<?> task) {
if (isShutdown())//如果线程池已经关闭,那么不能提交新任务
reject(task);//执行拒绝策略
else {
super.getQueue().add(task);//先添加到队列
// 再看看加入队列后发现池关闭的逻辑,先执行canRunInCurrentRunState(task.isPeriodic()):
// 既然canRunInCurrentRunState返回了false,则需要从队列移除task:
// 既然已经移除成功,那么这个task则肯定不会执行到了,但提交任务函数总会返回这个ScheduledFuture对象的,
// 所以我们必须告诉调用者这个信息(线程池已经关闭),通过调用task.cancel(false)。
if (isShutdown() &&//添加后发现线程池关闭
!canRunInCurrentRunState(task.isPeriodic()) &&//判断该任务在shutdown后,是否还可以执行
remove(task))//上面判断不能执行,那么remove这个task
task.cancel(false);//取消这个task
else
// 我们先看正常逻辑,不管线程池关闭的情况,那么加入task队列后,执行ensurePrestart。
// 可以发现ScheduledThreadPoolExecutor的提交任务的策略很简单:
// 先把task入队队列。
// 增加一个线程,直到线程数量达到corePoolSize。
ensurePrestart();//启动新线程,去获得task来执行
}
}
/**
* 除非当前运行状态不允许,否则将周期性任务重新队列。
* 与delayedExecute的想法相同,只不过是丢弃任务而不是拒绝。
*
* @param task the task
*/
void reExecutePeriodic(RunnableScheduledFuture<?> task) {
if (canRunInCurrentRunState(true)) {//判断当前线程池状态,是否可以入队
super.getQueue().add(task);//任务入列
//如果入队后发现线程池状态变了,则先移除task,再取消它。这和提交任务的逻辑一样
if (!canRunInCurrentRunState(true) && remove(task))
task.cancel(false);
else
ensurePrestart();//启动一个新的线程等待任务
}
}
/**
* 取消并清除由于关机策略而不应运行的所有任务的队列。在super.shutdown调用。
*/
@Override void onShutdown() {
// 这使得ScheduledThreadPoolExecutor的shutdown的逻辑完全不一样了,因为线程池shutdown后,
// 剩余任务不再是肯定会执行到的了,而是根据两个run-after-shutdown参数
//(continueExistingPeriodicTasksAfterShutdown和executeExistingDelayedTasksAfterShutdown)来做决定。
BlockingQueue<Runnable> q = super.getQueue();
boolean keepDelayed =
getExecuteExistingDelayedTasksAfterShutdownPolicy();
boolean keepPeriodic =
getContinueExistingPeriodicTasksAfterShutdownPolicy();
if (!keepDelayed && !keepPeriodic) {//如果延时任务和周期任务都不允许在Shutdown后被执行,那好办,无差别取消每个任务
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) {
RunnableScheduledFuture<?> t =
(RunnableScheduledFuture<?>)e;
if ((t.isPeriodic() ? !keepPeriodic : !keepDelayed) || //根据延时任务还是周期任务
t.isCancelled()) { // 即使task已经取消掉了,也要移除它
if (q.remove(t))
t.cancel(false);
}
}
}
}
tryTerminate();//帮助终止线程池
}
/**
* 修改或替换用于执行runnable的任务。
* 此方法可用于覆盖用于管理内部任务的具体类。默认实现只是返回给定的任务。
*
* @param runnable the submitted Runnable
* @param task the task created to execute the runnable
* @param <V> the type of the task's result
* @return a task that can execute the runnable
* @since 1.6
*/
protected <V> RunnableScheduledFuture<V> decorateTask(
Runnable runnable, RunnableScheduledFuture<V> task) {
return task;
}
/**
* 修改或替换用于执行可调用对象的任务。
* 此方法可用于覆盖用于管理内部任务的具体类。默认实现只是返回给定的任务。
*
* @param callable the submitted Callable
* @param task the task created to execute the callable
* @param <V> the type of the task's result
* @return a task that can execute the callable
* @since 1.6
*/
protected <V> RunnableScheduledFuture<V> decorateTask(
Callable<V> callable, RunnableScheduledFuture<V> task) {
return task;
}
/**
* 使用给定的核心池大小创建一个新的ScheduledThreadPoolExecutor。
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @throws IllegalArgumentException if {@code corePoolSize < 0}
*/
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
/**
* 使用给定的初始参数创建一个新的ScheduledThreadPoolExecutor。
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param threadFactory the factory to use when the executor
* creates a new thread
* @throws IllegalArgumentException if {@code corePoolSize < 0}
* @throws NullPointerException if {@code threadFactory} is null
*/
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue(), threadFactory);
}
/**
* 使用给定的初始参数创建一个新的ScheduledThreadPoolExecutor。
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param handler the handler to use when execution is blocked
* because the thread bounds and queue capacities are reached
* @throws IllegalArgumentException if {@code corePoolSize < 0}
* @throws NullPointerException if {@code handler} is null
*/
public ScheduledThreadPoolExecutor(int corePoolSize,
RejectedExecutionHandler handler) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue(), handler);
}
/**
* 使用给定的初始参数创建一个新的ScheduledThreadPoolExecutor。
*
* 在上一篇文章中我们知道execute中的执行策略受到这三个成员:
* corePoolSize、maximumPoolSize、阻塞队列 的影响。
* 但从这个构造器中可以发现maximumPoolSize和阻塞队列都已经被写死了。
*
* 并且由于重写了execute,现在ScheduledThreadPoolExecutor的执行策略是:
* 先让线程数量增加到corePoolSize,之后不会再增加。
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param threadFactory the factory to use when the executor
* creates a new thread
* @param handler the handler to use when execution is blocked
* because the thread bounds and queue capacities are reached
* @throws IllegalArgumentException if {@code corePoolSize < 0}
* @throws NullPointerException if {@code threadFactory} or
* {@code handler} is null
*/
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue(), threadFactory, handler);
}
方法2个triggerTime,overflowFree,4个scheduleXXX,execute,3个submit
/**
* 返回延迟操作的触发时间。
*/
private long triggerTime(long delay, TimeUnit unit) {
return triggerTime(unit.toNanos((delay < 0) ? 0 : delay));
}
/**
* 返回延迟操作的触发时间。
*/
long triggerTime(long delay) {
return now() +
((delay < (Long.MAX_VALUE >> 1)) ? delay : overflowFree(delay));
}
/**
* 将队列中所有延迟的值约束为Long.MAX_VALUE,以避免在compareTo中溢出。
* 如果一个任务有资格被退出队列,但还没有,而另一个任务被添加,延迟+long . max_value,则可能发生这种情况。
*/
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;
}
/**
* @throws RejectedExecutionException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
public ScheduledFuture<?> schedule(Runnable command,
long delay,
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
// command, null会用适配器合成一个Callable出来。
// triggerTime(delay, unit)得出任务可以执行的绝对时间。
// decorateTask的默认实现是直接返回第二个参数,我们知道第二个实参也是经过第一个实参才构造出来的。
// 所以我们可以自己重写decorateTask,忽略掉第二个参数,然后通过第一个参数构造出一个自定义实现的RunnableScheduledFuture。
RunnableScheduledFuture<?> t = decorateTask(command,
new ScheduledFutureTask<Void>(command, null,
triggerTime(delay, unit)));
delayedExecute(t);
return t;
}
/**
* @throws RejectedExecutionException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
public <V> ScheduledFuture<V> schedule(Callable<V> callable,
long delay,
TimeUnit unit) {
if (callable == null || unit == null)
throw new NullPointerException();
RunnableScheduledFuture<V> t = decorateTask(callable,
new ScheduledFutureTask<V>(callable,
triggerTime(delay, unit)));
delayedExecute(t);
return t;
}
/**
* @throws RejectedExecutionException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
* @throws IllegalArgumentException {@inheritDoc}
*/
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
long initialDelay,
long period,
TimeUnit unit) {
// 在一定条件下,这个函数的周期任务,当前周期的 开始时刻 与下一个周期的开始时刻总是相差period单位时间。
// 多了一个参数unit.toNanos(period)设置周期,为正数代表是FixedRate。
// 由于decorateTask默认实现返回第二个实参本身,且ScheduledFutureTask的outerTask成员默认初始化为this,所以这里的sft.outerTask = t实际上没有作用。
// 由于decorateTask的实现可能会被修改,所以delayedExecute(t)的实参一定要是decorateTask的返回值。
// 这个函数的周期任务,当前周期的 结束时刻 与下一个周期的开始时刻总是相差period单位时间。即这个函数是考虑了每个周期的执行时间了的。
if (command == null || unit == null)
throw new NullPointerException();
if (period <= 0)
throw new IllegalArgumentException();
ScheduledFutureTask<Void> sft =
new ScheduledFutureTask<Void>(command,
null,
triggerTime(initialDelay, unit),
unit.toNanos(period));
RunnableScheduledFuture<Void> t = decorateTask(command, sft);
sft.outerTask = t;
delayedExecute(t);
return t;
}
/**
* @throws RejectedExecutionException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
* @throws IllegalArgumentException {@inheritDoc}
*/
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
long initialDelay,
long delay,
TimeUnit unit) {
// 多了一个参数unit.toNanos(period)设置周期,为负数代表是FixedDelay。
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;
}
/**
* 执行命令不需要延迟。这相当于schedule(command, 0, anyUnit)。
* 注意,对shutdownNow返回的队列和列表的检查将访问零延迟的ScheduledFuture,而不是command本身。
*
* <p>使用ScheduledFuture对象的一个后果是,调用afterExecute时总是带有一个knull秒的Throwable参数,即使命令突然终止。
* 相反,这样一个任务的抛出的Throwable可以通过Future.get获得。
*
* @throws RejectedExecutionException at discretion of
* {@code RejectedExecutionHandler}, if the task
* cannot be accepted for execution because the
* executor has been shut down
* @throws NullPointerException {@inheritDoc}
*/
public void execute(Runnable command) {
// execute就相当于执行一个延时0秒的延时任务。
schedule(command, 0, NANOSECONDS);
}
// 覆盖AbstractExecutorService方法
/**
* @throws RejectedExecutionException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
public Future<?> submit(Runnable task) {
// 相比上一个,返回了schedule的返回值。
return schedule(task, 0, NANOSECONDS);
}
/**
* @throws RejectedExecutionException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
public <T> Future<T> submit(Runnable task, T result) {
// Executors.callable(task, result)适配成一个Callable。
return schedule(Executors.callable(task, result), 0, NANOSECONDS);
}
/**
* @throws RejectedExecutionException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
public <T> Future<T> submit(Callable<T> task) {
return schedule(task, 0, NANOSECONDS);
}
方法3个setXXX/getXXX,shutdown,shutdownNow,getQueue
/**
* 设置是否继续执行现有的定期任务的策略,即使这个执行程序已经关闭。
* 在这种情况下,这些任务只会在shutdownNow时终止,或者在已经关闭时将策略设置为false后才终止。
* 该值默认为false。
*
* @param value if {@code true}, continue after shutdown, else don't
* @see #getContinueExistingPeriodicTasksAfterShutdownPolicy
*/
public void setContinueExistingPeriodicTasksAfterShutdownPolicy(boolean value) {
continueExistingPeriodicTasksAfterShutdown = value;
if (!value && isShutdown())
onShutdown();
}
/**
* Gets the policy on whether to continue executing existing
* periodic tasks even when this executor has been {@code shutdown}.
* In this case, these tasks will only terminate upon
* {@code shutdownNow} or after setting the policy to
* {@code false} when already shutdown.
* This value is by default {@code false}.
*
* @return {@code true} if will continue after shutdown
* @see #setContinueExistingPeriodicTasksAfterShutdownPolicy
*/
public boolean getContinueExistingPeriodicTasksAfterShutdownPolicy() {
return continueExistingPeriodicTasksAfterShutdown;
}
/**
* 设置是否执行现有的延时任务的策略,即使此执行程序已关闭。
* 在这种情况下,这些任务只会在shutdownNow时终止,或者在已经关闭时将策略设置为false后终止。该值默认为true。
*
* @param value if {@code true}, execute after shutdown, else don't
* @see #getExecuteExistingDelayedTasksAfterShutdownPolicy
*/
public void setExecuteExistingDelayedTasksAfterShutdownPolicy(boolean value) {
executeExistingDelayedTasksAfterShutdown = value;
if (!value && isShutdown())
onShutdown();
}
/**
* Gets the policy on whether to execute existing delayed
* tasks even when this executor has been {@code shutdown}.
* In this case, these tasks will only terminate upon
* {@code shutdownNow}, or after setting the policy to
* {@code false} when already shutdown.
* This value is by default {@code true}.
*
* @return {@code true} if will execute after shutdown
* @see #setExecuteExistingDelayedTasksAfterShutdownPolicy
*/
public boolean getExecuteExistingDelayedTasksAfterShutdownPolicy() {
return executeExistingDelayedTasksAfterShutdown;
}
/**
* 设置取消任务在取消时是否应立即从工作队列中删除的策略。该值默认为false。
*
* @param value if {@code true}, remove on cancellation, else don't
* @see #getRemoveOnCancelPolicy
* @since 1.7
*/
public void setRemoveOnCancelPolicy(boolean value) {
removeOnCancel = value;
}
/**
* Gets the policy on whether cancelled tasks should be immediately
* removed from the work queue at time of cancellation. This value is
* by default {@code false}.
*
* @return {@code true} if cancelled tasks are immediately removed
* from the queue
* @see #setRemoveOnCancelPolicy
* @since 1.7
*/
public boolean getRemoveOnCancelPolicy() {
return removeOnCancel;
}
/**
* 启动一个有序的关闭,在此关闭中执行先前提交的任务,但不接受新的任务。
* 如果已经关闭,则调用没有额外的效果。
*
* <p>此方法不等待以前提交的任务完成执行。使用awaitTermination来做这个。
*
* <p>如果ExecuteExistingDelayedTasksAfterShutdownPolicy被设置为false,则现有的延迟未经过的任务将被取消。
* 并且除非ContinueExistingPeriodicTasksAfterShutdownPolicy被设置为true,否则将取消现有的定时任务的未来执行。
*
* @throws SecurityException {@inheritDoc}
*/
public void shutdown() {
// 内部有 有一个钩子方法,ScheduledThreadPoolExecutor把onShutdown实现了
super.shutdown();
}
/**
* 尝试停止所有正在积极执行的任务,停止等待任务的处理,并返回一个正在等待执行的任务列表。
*
* <p>此方法不等待主动执行的任务终止。使用awaitTermination来完成。
*
* <p>除了尽最大努力停止积极执行任务的处理之外,没有任何保证。
* 该实现通过Thread.interrupt取消任务,因此任何未能响应中断的任务可能永远不会终止。
*
* @return list of tasks that never commenced execution.
* Each element of this list is a {@link ScheduledFuture},
* including those tasks submitted using {@code execute},
* which are for scheduling purposes used as the basis of a
* zero-delay {@code ScheduledFuture}.
* @throws SecurityException {@inheritDoc}
*/
public List<Runnable> shutdownNow() {
return super.shutdownNow();
}
/**
* 返回此执行程序使用的任务队列。这个队列的每个元素都是一个ScheduledFuture,
* 包括那些使用execute提交的任务,它们是用于调度目的的,用作零延迟ScheduledFuture的基础。
* 这个队列上的迭代不能保证按照任务执行的顺序遍历任务。
*
* @return the task queue
*/
public BlockingQueue<Runnable> getQueue() {
return super.getQueue();
}
内部类DelayedWorkQueue
/**
* 专门的延迟队列。为了与TPE声明相匹配,
* 这个类必须被声明为BlockingQueue,即使它只能保存RunnableScheduledFutures。
*
* 内部类DelayedWorkQueue实现了一个延时优先队列,它完全类似于DelayQueue,所以本文不会讲解这部分内容。
* 唯一一点不同是,它会节点所在数组索引记录在ScheduledFutureTask节点的heapIndex成员上,它会时刻保持节点的数组索引正确。
* 节点在堆中时,索引为非负数;节点被删除时,索引为-1。
* 堆中查找节点时,时间复杂度为O ( 1 )。
*/
static class DelayedWorkQueue extends AbstractQueue<Runnable>
implements BlockingQueue<Runnable> {
/*
* A DelayedWorkQueue is based on a heap-based data structure
* like those in DelayQueue and PriorityQueue, except that
* every ScheduledFutureTask also records its index into the
* heap array. This eliminates the need to find a task upon
* cancellation, greatly speeding up removal (down from O(n)
* to O(log n)), and reducing garbage retention that would
* otherwise occur by waiting for the element to rise to top
* before clearing. But because the queue may also hold
* RunnableScheduledFutures that are not ScheduledFutureTasks,
* we are not guaranteed to have such indices available, in
* which case we fall back to linear search. (We expect that
* most tasks will not be decorated, and that the faster cases
* will be much more common.)
*
* All heap operations must record index changes -- mainly
* within siftUp and siftDown. Upon removal, a task's
* heapIndex is set to -1. Note that ScheduledFutureTasks can
* appear at most once in the queue (this need not be true for
* other kinds of tasks or work queues), so are uniquely
* identified by heapIndex.
*/
private static final int INITIAL_CAPACITY = 16;
private RunnableScheduledFuture<?>[] queue =
new RunnableScheduledFuture<?>[INITIAL_CAPACITY];
private final ReentrantLock lock = new ReentrantLock();
private int size = 0;
/**
* Thread designated to wait for the task at the head of the
* queue. This variant of the Leader-Follower pattern
* (http://www.cs.wustl.edu/~schmidt/POSA/POSA2/) serves to
* minimize unnecessary timed waiting. When a thread becomes
* the leader, it waits only for the next delay to elapse, but
* other threads await indefinitely. The leader thread must
* signal some other thread before returning from take() or
* poll(...), unless some other thread becomes leader in the
* interim. Whenever the head of the queue is replaced with a
* task with an earlier expiration time, the leader field is
* invalidated by being reset to null, and some waiting
* thread, but not necessarily the current leader, is
* signalled. So waiting threads must be prepared to acquire
* and lose leadership while waiting.
*/
private Thread leader = null;
/**
* Condition signalled when a newer task becomes available at the
* head of the queue or a new thread may need to become leader.
*/
private final Condition available = lock.newCondition();
/**
* Sets f's heapIndex if it is a ScheduledFutureTask.
*/
private void setIndex(RunnableScheduledFuture<?> f, int idx) {
if (f instanceof ScheduledFutureTask)
((ScheduledFutureTask)f).heapIndex = idx;
}
/**
* Sifts element added at bottom up to its heap-ordered spot.
* Call only when holding lock.
*/
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;
queue[k] = e;
setIndex(e, k);
k = parent;
}
queue[k] = key;
setIndex(key, k);
}
/**
* Sifts element added at top down to its heap-ordered spot.
* Call only when holding lock.
*/
private void siftDown(int k, RunnableScheduledFuture<?> key) {
int half = size >>> 1;
while (k < half) {
int child = (k << 1) + 1;
RunnableScheduledFuture<?> c = queue[child];
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);
}
/**
* Resizes the heap array. Call only when holding lock.
*/
private void grow() {
int oldCapacity = queue.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); // grow 50%
if (newCapacity < 0) // overflow
newCapacity = Integer.MAX_VALUE;
queue = Arrays.copyOf(queue, newCapacity);
}
/**
* Finds index of given object, or -1 if absent.
*/
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;
}
public boolean contains(Object x) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return indexOf(x) != -1;
} finally {
lock.unlock();
}
}
public boolean remove(Object x) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
int i = indexOf(x);
if (i < 0)
return false;
setIndex(queue[i], -1);
int s = --size;
RunnableScheduledFuture<?> replacement = queue[s];
queue[s] = null;
if (s != i) {
siftDown(i, replacement);
if (queue[i] == replacement)
siftUp(i, replacement);
}
return true;
} finally {
lock.unlock();
}
}
public int size() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return size;
} finally {
lock.unlock();
}
}
public boolean isEmpty() {
return size() == 0;
}
public int remainingCapacity() {
return Integer.MAX_VALUE;
}
public RunnableScheduledFuture<?> peek() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return queue[0];
} finally {
lock.unlock();
}
}
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);
} else {
siftUp(i, e);
}
if (queue[0] == e) {
leader = null;
available.signal();
}
} finally {
lock.unlock();
}
return true;
}
public void put(Runnable e) {
offer(e);
}
public boolean add(Runnable e) {
return offer(e);
}
public boolean offer(Runnable e, long timeout, TimeUnit unit) {
return offer(e);
}
/**
* Performs common bookkeeping for poll and take: Replaces
* first element with last and sifts it down. Call only when
* holding lock.
* @param f the task to remove and return
*/
private RunnableScheduledFuture<?> finishPoll(RunnableScheduledFuture<?> f) {
int s = --size;
RunnableScheduledFuture<?> x = queue[s];
queue[s] = null;
if (s != 0)
siftDown(0, x);
setIndex(f, -1);
return f;
}
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();
}
}
public RunnableScheduledFuture<?> take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
for (;;) {
RunnableScheduledFuture<?> first = queue[0];
if (first == null)
available.await();
else {
long delay = first.getDelay(NANOSECONDS);
if (delay <= 0)
return finishPoll(first);
first = null; // don't retain ref while waiting
if (leader != null)
available.await();
else {
Thread thisThread = Thread.currentThread();
leader = thisThread;
try {
available.awaitNanos(delay);
} finally {
if (leader == thisThread)
leader = null;
}
}
}
}
} finally {
if (leader == null && queue[0] != null)
available.signal();
lock.unlock();
}
}
public RunnableScheduledFuture<?> poll(long timeout, TimeUnit unit)
throws InterruptedException {
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
for (;;) {
RunnableScheduledFuture<?> first = queue[0];
if (first == null) {
if (nanos <= 0)
return null;
else
nanos = available.awaitNanos(nanos);
} else {
long delay = first.getDelay(NANOSECONDS);
if (delay <= 0)
return finishPoll(first);
if (nanos <= 0)
return null;
first = null; // don't retain ref while waiting
if (nanos < delay || leader != null)
nanos = available.awaitNanos(nanos);
else {
Thread thisThread = Thread.currentThread();
leader = thisThread;
try {
long timeLeft = available.awaitNanos(delay);
nanos -= delay - timeLeft;
} finally {
if (leader == thisThread)
leader = null;
}
}
}
}
} finally {
if (leader == null && queue[0] != null)
available.signal();
lock.unlock();
}
}
public void clear() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
for (int i = 0; i < size; i++) {
RunnableScheduledFuture<?> t = queue[i];
if (t != null) {
queue[i] = null;
setIndex(t, -1);
}
}
size = 0;
} finally {
lock.unlock();
}
}
/**
* Returns first element only if it is expired.
* Used only by drainTo. Call only when holding lock.
*/
private RunnableScheduledFuture<?> peekExpired() {
// assert lock.isHeldByCurrentThread();
RunnableScheduledFuture<?> first = queue[0];
return (first == null || first.getDelay(NANOSECONDS) > 0) ?
null : first;
}
public int drainTo(Collection<? super Runnable> c) {
if (c == null)
throw new NullPointerException();
if (c == this)
throw new IllegalArgumentException();
final ReentrantLock lock = this.lock;
lock.lock();
try {
RunnableScheduledFuture<?> first;
int n = 0;
while ((first = peekExpired()) != null) {
c.add(first); // In this order, in case add() throws.
finishPoll(first);
++n;
}
return n;
} finally {
lock.unlock();
}
}
public int drainTo(Collection<? super Runnable> c, int maxElements) {
if (c == null)
throw new NullPointerException();
if (c == this)
throw new IllegalArgumentException();
if (maxElements <= 0)
return 0;
final ReentrantLock lock = this.lock;
lock.lock();
try {
RunnableScheduledFuture<?> first;
int n = 0;
while (n < maxElements && (first = peekExpired()) != null) {
c.add(first); // In this order, in case add() throws.
finishPoll(first);
++n;
}
return n;
} finally {
lock.unlock();
}
}
public Object[] toArray() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return Arrays.copyOf(queue, size, Object[].class);
} finally {
lock.unlock();
}
}
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (a.length < size)
return (T[]) Arrays.copyOf(queue, size, a.getClass());
System.arraycopy(queue, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
} finally {
lock.unlock();
}
}
public Iterator<Runnable> iterator() {
return new Itr(Arrays.copyOf(queue, size));
}
/**
* Snapshot iterator that works off copy of underlying q array.
*/
private class Itr implements Iterator<Runnable> {
final RunnableScheduledFuture<?>[] array;
int cursor = 0; // index of next element to return
int lastRet = -1; // index of last element, or -1 if no such
Itr(RunnableScheduledFuture<?>[] array) {
this.array = array;
}
public boolean hasNext() {
return cursor < array.length;
}
public Runnable next() {
if (cursor >= array.length)
throw new NoSuchElementException();
lastRet = cursor;
return array[cursor++];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
DelayedWorkQueue.this.remove(array[lastRet]);
lastRet = -1;
}
}
}