一、概述
ScheduledThreadPoolExecutor继承了ThreadPoolExecutor;内部扩展了任务的ScheduledFutureTask类实现运行任务后修改下一次执行时间;对队列内部采用了再次继承封装的DelayQueue队列,运用了DelayQueue队列的添加排序特性根据最先运行时间进行排序,运用了DelayQueue只有在延迟期满时才能从中提取元素,并且该队列的头部 是延迟期满后保存时间最长的 Delayed 元素。
二、添加一个定时任务过程:
1、形成ScheduledFutureTask对象,该对象实现了Delayed接口的compareTo(Delayed)比较方法和getDelay(TimeUnit unit)获取延迟时间方法。
2、//如果线程数<核心线程数,创建一个空任务的线程并放入到hashset保存,并启动线程。其实是调用的ThreadPoolExecutor里的addIfUnderCorePoolSize(null)方法
3、添加任务到DelayedWorkQueue队列。上面已经新建线程到线程池并已经启动,线程池里的线程不断轮询是否可以取到任务,如果可以取到任务便执行任务,如果不可以取到便阻塞take方法上。这个轮询取任务过程是用的ThreadPoolExecutor里的过程,和普通的一样。区别在于DelayedWorkQueue队列的take方法,该方法在任务未到期时是无法取到任务的。
4、ScheduledFutureTask内部对run进行再次封装,如果任务是非周期的直接执行;如果是周期任务,执行完成任务后,修改下一次的time time += 执行周期,并再次添加到队列里;
概括:把任务包装形成ScheduledFutureTask对象,如果目前线程数<核心线程数借助线程池创建空任务线程并激活,把ScheduledFutureTask对象添加到DelayedQueue延迟队列。线程池线程轮询队列,如果任务延迟时间到期进行出队运行任务,否则阻塞等待。
public class ScheduledThreadPoolExecutor
extends ThreadPoolExecutor //继承ThreadPoolExecutorimplements ScheduledExecutorService {
// DelayedWorkQueue是一个无界队列,max线程数无用。
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,new DelayedWorkQueue());
}
添加定时执行任务的方法如下:
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对象
RunnableScheduledFuture<?> t = decorateTask(command,new ScheduledFutureTask<Object>(command,
null,
triggerTime(initialDelay, unit),
unit.toNanos(period)));
delayedExecute(t); //延迟执行任务
return t;
}
private void delayedExecute(Runnable command) {
if (isShutdown()) {//如果不是running状态,把任务交给拒绝策略
reject(command);
return;
}
// Prestart a thread if necessary. We cannot prestart it
// running the task because the task (probably) shouldn't be
// run yet, so thread will just idle until delay elapses.
if (getPoolSize() < getCorePoolSize())//如果线程数<核心线程数,创建一个空任务的线程并放入到hashset保存,并启动线程。其实是调用的ThreadPoolExecutor里的addIfUnderCorePoolSize(null)方法
prestartCoreThread();
super.getQueue().add(command);//添加任务到DelayedWorkQueue队列。上面已经新建线程到线程池并已经启动,线程池里的线程不断轮询是否可以取到任务,如果可以取到任务便执行任务,如果不可以取到便阻塞take方法上。这个轮询取任务过程是用的ThreadPoolExecutor里的过程,和普通的一样。区别在于DelayedWorkQueue队列的take方法,该方法在任务未到期时是无法取到任务的。
}
----------------------------------------------------------------------------------------
ScheduledFutureTask内部的结构如下:
private final long sequenceNumber;//任务的序列号,当任务的时间相同时,序列号的小先执行。
private long time;//任务的执行时间
private final long period; //任务的执行间隔周期
/**
* Overrides FutureTask version so as to reset/requeue if periodic.*/
public void run() {
if (isPeriodic())//是否周期任务,根据period是否=0
runPeriodic();//执行任务后,修改下一次的time
else
ScheduledFutureTask.super.run();//不是周期任务,直接执行任务的方法
}
private void runPeriodic() {
boolean ok = ScheduledFutureTask.super.runAndReset();//执行任务
boolean down = isShutdown();//是否运行shutdown
// Reschedule if not cancelled and not shutdown or policy allows
if (ok && (!down ||
(getContinueExistingPeriodicTasksAfterShutdownPolicy() &&
!isStopped()))) {
long p = period;
if (p > 0)
time += p;//根据上一次时间,修改下一次的time。如果间隔为5,本次2、7、12、17,每次间隔固定不变
else
time = triggerTime(-p);//?????根据当前时间+P时间间隔算出下一次时间。如果间隔为5,本次为2下次运行7,由于未获得线程在8运行,下下次运行为8+5=13
ScheduledThreadPoolExecutor.super.getQueue().add(this);//再把任务添加到队列
}
// This might have been the final executed delayed
// task. Wake up threads to check.
else if (down)
interruptIdleWorkers();//中断非工作中线程,通过是否获取到线程的运行锁来判断线程是否工作
}
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit)依据固定速率执行,列入间隔为5,严格按照2、7、12执行
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,long initialDelay,long delay, TimeUnit unit)依据固定间隔执行,例如间隔为5秒 2开始执行第一次执行后为8秒,第二次执行为8+5=13秒
public void shutdown() {
cancelUnwantedTasks();根据策略是否移除队列里任务。
super.shutdown();
}