ScheduledThreadExecutor是在TPE的基础上新增了让任务在指定延迟以后执行,或者周期性执行的功能。它比Timer更加灵活,可以多线程等等。
延迟的任务在被允许之前不能执行,但是不能保证,在允许之后多久能真正执行到。任务按照提交顺序的FIFO顺序调度。当有任务在运行之前被取消,执行将会被停止。默认情况下此时任务不会从任务队列中移除,除非到了延迟的时间。通过设置setRemoveOnCancelPolicy可以让任务在被取消的时候从队列移除。
一个任务的执行通过scheduleAtFixedRate或者scheduleWithFixedDelay调度到,不会重合。但是不同的调度可能由不同的线程执行,在前面执行的在内存可见性意义上happen-before后面的执行。
虽然这个类继承自TPE,但是一些方法不会用到,比如,这个类拥有的是固定大小的线程池,并且有一个无界的队列。
STPE类重写了TPE的execute(Runnable)方法和AbstractExecutorService的submit(Runnable)方法,用来生成ScheduledFuture对象来控制每个任务的延迟和调度。并且,后面继承的类不能改写这个方法(只能使用父类的逻辑)。但是提供了decorateTask方法来定制具体的任务类型。(customize the concrete task types used to execute commands entered execute等方法)
STPE对TPE做了如下改变
- 使用自定义的任务类型ScheduledFutureTask来代表任务,即使这些任务不需要调度执行(比如是通过TPE的execute的方法调用的,而不是ScheduledExecutorService的方法)。这些任务被视为延迟为0的任务。
- 使用了自定义的队列DelayedWorkQueue,是无界的DelayQueue的一个变种。
- 支持run-after-shutdown参数。能覆盖shutdown方法来让任务在shutdown以后不再执行
构造参数
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue(), threadFactory, handler);
}
就是TPE的构造方法,不再赘述。
提交任务的方法
- schedule系列方法
public ScheduledFuture<?> schedule(Runnable command,
long delay,
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
RunnableScheduledFuture<?> t = decorateTask(command,
new ScheduledFutureTask<Void>(command, null,
triggerTime(delay, unit)));
delayedExecute(t);
return t;
}
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;
}
- schedule周期性执行
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();
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;
}
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;
}
- execute方法
public void execute(Runnable command) {
schedule(command, 0, NANOSECONDS);
}
- submit方法
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);
}
public <T> Future<T> submit(Callable<T> task) {
return schedule(task, 0, NANOSECONDS);
}
无论是什么提交任务的方法,都离不开ScheduledFutureTask这个类。
private class ScheduledFutureTask<V>
extends FutureTask<V> implements RunnableScheduledFuture<V>
这个类是是FutureTask和RunnableScheduledFuture的子类,看他的构造方法
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();
}
ScheduledFutureTask(Callable<V> callable, long ns) {
super(callable);
this.time = ns;
this.period = 0;
this.sequenceNumber = sequencer.getAndIncrement();
}
都会调到父类的方法,那就看一下父类FutureTask。FutureTask是Future类的子类。Future描述了一个异步计算,通过get()方法能够获得计算结果(否则阻塞在这里)。另外,Future也支持cancel。Future支持泛型T,表示返回结果的类型。如果只是想用Future表示一个可cancel的计算,并不关心返回值,可以将Future定义为Future<?>,并且将任务的计算结果返回null.FutureTask是Future的一种实现,并且可以被Executor执行
FutureTask<String> future = new FutureTask<String>(new Callable<String>() {
public String call() {
return "hello world";
}
});
//因此FutureTask实现了Runnable接口
executor.execute(future);
关于FutureTask的实现后面有机会再介绍。ScheduledFutureTask相对于父类FutureTask多了几个成员变量
- int sequenceNumber
- long time, 代表任务允许执行的时间
- long period, 代表任务重复的周期。period>0表示固定速率执行,period<0表示固定延迟执行。
- RunnableScheduledFuture outerTask
- int heapIndex
通过time和period可以控制周期性任务下次执行的时间
private void setNextRunTime() {
long p = period;
if (p > 0)
//在上一次执行的基础上
time += p;
else
//只管触发时间,不用关心上次执行时间
time = triggerTime(-p);
}
long triggerTime(long delay) {
return now() +
((delay < (Long.MAX_VALUE >> 1)) ? delay : overflowFree(delay));
}
值得注意的是,SFT有一个compareTo方法,(谁的time小谁排在前面?)
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;
}
ScheduledFutureTask本质上来说还是一个FutureTask,它也有一个run方法
public void run() {
boolean periodic = isPeriodic();
if (!canRunInCurrentRunState(periodic))
cancel(false);
else if (!periodic)
ScheduledFutureTask.super.run();
//运行一次,然后回到最初的状态
else if (ScheduledFutureTask.super.runAndReset()) {
setNextRunTime();
reExecutePeriodic(outerTask);
}
}
运行一次以后将任务再排进队列:
void reExecutePeriodic(RunnableScheduledFuture<?> task) {
if (canRunInCurrentRunState(true)) {
super.getQueue().add(task);
if (!canRunInCurrentRunState(true) && remove(task))
task.cancel(false);
else
ensurePrestart();
}
}
因此SFT和FutureTask比有两个不同
- 配置了下一次执行的时间
- 运行完重新塞进队列中
那么,什么时候运行呢? 那就要看队列DelayedWorkQueue怎么实现的了。
DelayedWorkQueue
- 首先它是一种BlockingQueue,为了兼容TPE,它被定义为BlockingQueue的实现,其实它仅仅被用来存放RunnableScheduledFutures
- 它的内部有一个数组用来实现一个堆(序号为x的节点的parent为(x-1)>>>1)
RunnableScheduledFuture<?>[] queue =
new RunnableScheduledFuture<?>[INITIAL_CAPACITY];
- 它内部节点自己保存了一份自己的index,这样在cancel的时候加速寻找(O(n) -> O(log n)
插入一个节点时进堆时,将其提升
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);
}
给一个节点降级
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;
//right<size,别出界。
if (right < size && c.compareTo(queue[right]) > 0)
//右节点更靠前,赋给c
c = queue[child = right];
//已经比子节点更靠前了,不能再往后降了
if (key.compareTo(c) <= 0)
break;
queue[k] = c;
setIndex(c, k);
//k是刚刚新给key的位置,后面可能还会继续降级
k = child;
}
queue[k] = key;
setIndex(key, k);
}