类关系
public class ScheduledThreadPoolExecutor
extends ThreadPoolExecutor
implements ScheduledExecutorService {}
可以看出,ScheduledThreadPoolExecutor继承了ThreadPoolExecutor类,实现了ScheduledExecutorService接口。其构造函数如下:
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
构造函数中调用了super方法,super方法其实是ThreadPoolExecutor类的构造函数,如下:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
从上述关系看出,ScheduledThreadPoolExecutor其实是ThreadPoolExecutor线程池衍生品,使用了DelayedWorkQueue线程池队列,线程池容量无边界,添加了延迟后运行命令,或者定期执行命令等属性。
一个例子
import java.util.concurrent.*;
public class ScheduleTest {
public static void main(String[] args) throws InterruptedException,ExecutionException{
ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(10);
executScheduleWithFixedDelay(executorService);
executScheduleAtFixedRate(executorService);
executSchedule(executorService);
System.out.println("over");
executorService.shutdown();
}
// 间隔3秒执行一次
public static void executScheduleWithFixedDelay(ScheduledExecutorService executorService) throws InterruptedException,ExecutionException{
ScheduledFuture<?> result = executorService.scheduleWithFixedDelay(
new Runnable() {
@Override
public void run() {
System.out.println("scheduleWithFixedDelay-" + System.currentTimeMillis());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},1000, 3000,TimeUnit.MILLISECONDS
);
result.get();
}
// 线程在第4秒开始执行
public static void executScheduleAtFixedRate(ScheduledExecutorService executorService) throws InterruptedException,ExecutionException{
ScheduledFuture<?> result = executorService.scheduleAtFixedRate(
new Runnable() {
@Override
public void run() {
System.out.println("scheduleAtFixedRate-" + System.currentTimeMillis());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},1000, 4000,TimeUnit.MILLISECONDS
);
result.get();
}
// 线程延迟4秒执行,仅执行一次
public static void executSchedule(ScheduledExecutorService executorService) throws InterruptedException,ExecutionException{
ScheduledFuture<?> result = executorService.schedule(
new Runnable() {
@Override
public void run() {
System.out.println("schedule-" + System.currentTimeMillis());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},4000, TimeUnit.MILLISECONDS
);
result.get();
}
}
例子中也可以调用Executors的静态方法获取ScheduledExecutorService实例:
ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
这个例子引出了ScheduledThreadPoolExecutor调度线程池启动的3方法:
- scheduleWithFixedDelay:线程延迟执行。
- scheduleAtFixedRate:线程依照时间表执行,如果到指定时间点线程还没有执行完毕,则放弃该时间点,执行完毕后继续执行下一个线程。
- schedule:创建并执行在给定延迟后启用的一次性操作。
相关方法执行结果参考:
源码说明
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;
}
任务添加到队列后,工作线程会从队列获取并移除到期的元素,然后执行run方法,所以下面看看ScheduledFutureTask的run方法如何实现定时调度的。
其中ScheduledFutureTask封装定时任务内部类,重点关注其run方法。
ScheduledFutureTask(Runnable r, V result, long ns, long period) {
super(r, result);
this.time = ns;
this.period = period;
this.sequenceNumber = sequencer.getAndIncrement();
}
public void run() {
boolean periodic = isPeriodic();
if (!canRunInCurrentRunState(periodic))
cancel(false);
//仅执行一次
else if (!periodic)
ScheduledFutureTask.super.run();
//定时任务
else if (ScheduledFutureTask.super.runAndReset()) {
setNextRunTime();
//重新加入该任务到delay队列
reExecutePeriodic(outerTask);
}
}
定时调度是先从队列获取任务然后执行,然后在重新设置任务时间,在把任务放入队列实现的。
如果任务执行时间大于delay时间则等任务执行完毕后的delay时间后在次调用任务,不会同一个任务并发执行。
scheduleAtFixedRate、schedule方法原理如出一辙。