三十六、线程池之ScheduledThreadPoolExecutor

50 阅读6分钟

ScheduledThreadPoolExecutor

这是一个执行延迟任务或者周期性任务的线程池

ScheduledThreadPoolExecutor的三个参数

public ScheduledThreadPoolExecutor(int corePoolSize,
								   ThreadFactory threadFactory,
								   RejectedExecutionHandler handler) {
	super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
		  new DelayedWorkQueue(), threadFactory, handler);
}
  • corePoolSize:核心工作线程数
  • threadFactory:线程工厂
  • handler:拒绝策略

ScheduledThreadPoolExecutor默认使用的是DelayedWorkQueue队列,这是基于DelayedQueue实现的延迟队列,而且是无界队列。

ScheduledThreadPoolExecutor的核心属性

// 这些属性是任务取消时需要使用
private volatile boolean continueExistingPeriodicTasksAfterShutdown;
private volatile boolean executeExistingDelayedTasksAfterShutdown = true;
private volatile boolean removeOnCancel = false;

// 计数器。如果两个任务执行时间一模一样,用这个来判断谁先执行
private static final AtomicLong sequencer = new AtomicLong();

ScheduledFutureTask内部类:

// 定时任务线程池可以传Runable任务,也可以传Callable任务
private class ScheduledFutureTask<V>
		extends FutureTask<V> implements RunnableScheduledFuture<V> {

	// 如果两个任务执行时间相同,用这个属性判断谁先执行
	private final long sequenceNumber;

	// 任务执行时间
	private long time;

	// period==0:代表此任务只执行一次
	// period>0:代表At,任务的间隔时间=周期时间
	// period<0:代表With,任务的间隔时间=任务执行时间+周期时间
	private final long period;

	// 封装的周期性执行的任务,通过此属性拿到任务放回阻塞队列
	RunnableScheduledFuture<V> outerTask = this;

	int heapIndex;

	// 构建延迟执行的任务
	ScheduledFutureTask(Runnable r, V result, long ns) {
		super(r, result);
		this.time = ns;
		this.period = 0;
		this.sequenceNumber = sequencer.getAndIncrement();
	}

	// 构建At或者With任务
	ScheduledFutureTask(Runnable r, V result, long ns, long period) {
		super(r, result);
		this.time = ns;
		this.period = period;
		this.sequenceNumber = sequencer.getAndIncrement();
	}

	// 此处省略其他方法
}

ScheduledThreadPoolExecutor的核心方法

execute方法

public void execute(Runnable command) {
	schedule(command, 0, NANOSECONDS);
}

execute方法中调用的是schedule方法,延迟时间默认为0,所以任务会立即执行

schedule方法

  1. 如果任务为null或者时间单位为null,抛出异常
  2. 将任务执行时间计算出来
  3. 将任务封装成延迟执行任务
  4. 执行delayedExecute方法
// command:待执行的任务;delay:延迟时间;unit:时间单位
public ScheduledFuture<?> schedule(Runnable command,
								   long delay,
								   TimeUnit unit) {
	if (command == null || unit == null)
		// 任务为空,或者时间单位为空,抛出空指针异常
		throw new NullPointerException();
	// 将任务封装成RunnableScheduledFuture
	// triggerTime方法是将任务执行时间计算出来,并且对时间做合理性校验
	// ScheduledFutureTask构造方法将任务封装成延迟执行的任务
	// decorateTask方法的入参是原任务和封装好的任务
	// 但是此方法默认直接将封装好的任务返回,程序猿可以重写此方法
	RunnableScheduledFuture<?> t = decorateTask(command,
		new ScheduledFutureTask<Void>(command, null,
									  triggerTime(delay, unit)));
	// 执行任务
	delayedExecute(t);
	return t;
}

triggerTime方法:

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));
}

ScheduledFutureTask方法:

ScheduledFutureTask(Runnable r, V result, long ns) {
	// 因为传入的任务时Runnable,所以返回值result为null
	super(r, result);
	this.time = ns;
	// period为0说明是延迟任务
	this.period = 0;
	// 如果两个任务的执行时间相同,需要根据sequenceNumber判断谁先执行
	this.sequenceNumber = sequencer.getAndIncrement();
}

delayedExecute方法:

  1. 如果线程池状态是SHUTDOWN,执行拒绝策略
  2. 将任务放入延迟队列中
  3. 如果线程池状态是SHUTDOWN,并且在SHUTDOWN状态下,延迟任务不能执行,删除任务
  4. 否则执行ensurePrestart方法
private void delayedExecute(RunnableScheduledFuture<?> task) {
	if (isShutdown())
		// 线程池状态不为RUNNING,执行拒绝策略
		reject(task);
	else {
		// 将任务放入延迟队列
		super.getQueue().add(task);
		// 任务放到延迟队列中后,再次判断线程池状态是否为SHUTDOWN
		// 并且判断在SHUTDOWN状态下,队列中的延迟任务是否可以执行(默认情况下可以执行)
		if (isShutdown() &&
			// 默认情况下,SHUTDOWN状态下的延迟任务可以执行
			!canRunInCurrentRunState(task.isPeriodic()) &&
			// 如果延迟任务不能执行,删除任务
			remove(task))
			task.cancel(false);
		else
			// 执行任务
			ensurePrestart();
	}
}

isPeriodic方法:

// 任务是延迟任务,返回false
// 任务是周期任务,返回true
public boolean isPeriodic() {
	return period != 0;
}

canRunInCurrentRunState方法:

boolean canRunInCurrentRunState(boolean periodic) {
	return isRunningOrShutdown(periodic ?
								// continueExistingPeriodicTasksAfterShutdown值默认为false
								// executeExistingDelayedTasksAfterShutdown值默认为true
								// 说明默认情况下,在线程池为SHUTDOWN状态下
								// 周期任务不能执行,延迟任务可以执行
							   continueExistingPeriodicTasksAfterShutdown :
							   executeExistingDelayedTasksAfterShutdown);
}

isRunningOrShutdown方法:

final boolean isRunningOrShutdown(boolean shutdownOK) {
	// 获取线程池状态
	int rs = runStateOf(ctl.get());
	// 如果线程池状态是RUNNING,返回true
	// 如果线程池状态是SHUTDOWN,根据shutdownOK返回
	return rs == RUNNING || (rs == SHUTDOWN && shutdownOK);
}

ensurePrestart方法:

  1. 获取工作线程数
  2. 如果工作线程数小于最大核心工作线程数,创建核心工作线程执行任务
  3. 如果最大核心工作线程设置为0,创建普通工作线程执行任务
void ensurePrestart() {
	// 获得工作线程数
	int wc = workerCountOf(ctl.get());
	if (wc < corePoolSize)
		// 工作线程数小于核心工作线程最大数
		// 创建核心工作线程执行任务
		addWorker(null, true);
	else if (wc == 0)
		// 工作线程数为0,在不满足wc < corePoolSize条件的情况下
		// 说明核心工作线程最大数也设置为0
		// 创建普通工作线程执行任务
		addWorker(null, false);
}

scheduleAtFixedRate方法

scheduleAtFixedRate方法和scheduleWithFixedDelay方法在源码层面的区别就是计算周期时间时,传递给period属性的值一个是正数,一个是负数

  1. 如果任务为null,或者时间单位为null,抛出异常
  2. 如果周期时间小于0,抛出异常
  3. 将任务封装成周期任务
  4. 执行delayedExecute方法
// command:任务;initialDelay:第一次执行的延迟时间;period:周期时间;unit:时间单位
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
											  long initialDelay,
											  long period,
											  TimeUnit unit) {
	if (command == null || unit == null)
		throw new NullPointerException();
	if (period <= 0)
		// period大于0才是At任务,小于和等于0,直接抛异常
		throw new IllegalArgumentException();
	// 将任务封装成ScheduledFutureTask
	ScheduledFutureTask<Void> sft =
		new ScheduledFutureTask<Void>(command,
									  null,
									  triggerTime(initialDelay, unit),
									  unit.toNanos(period));
	// decorateTask方法返回封装后的任务,程序猿可以重写此方法
	RunnableScheduledFuture<Void> t = decorateTask(command, sft);
	// 周期任务在执行完后需要重新放回队列,将任务赋值给outerTask属性
	// 是为了重新拿任务时方便
	sft.outerTask = t;
	// 处理任务
	delayedExecute(t);
	return t;
}

无论是延迟任务还是周期任务,执行时都会调用ScheduledFutureTask的run方法

  1. 判断任务在当前线程池状态下是否可以执行
  2. 如果不能执行,取消任务
  3. 如果可以执行,延迟任务执行后,不做任何操作
  4. 周期任务执行后,计算出下次执行的时间,然后将任务放回队列
public void run() {
	boolean periodic = isPeriodic();
	if (!canRunInCurrentRunState(periodic))
		// 判断任务在线程池当前状态下是否可以执行
		cancel(false);
	else if (!periodic)
		// 任务为延迟执行任务,执行任务后什么也不用做
		ScheduledFutureTask.super.run();
	// runAndReset方法是执行任务的方法
	else if (ScheduledFutureTask.super.runAndReset()) {
		// 周期任务执行后,需要计算下次执行的时间
		setNextRunTime();
		// 将任务放回队列中
		reExecutePeriodic(outerTask);
	}
}

setNextRunTime方法:

private void setNextRunTime() {
	long p = period;
	if (p > 0)
		// At任务,下次执行时间=任务上次开始执行时间+间隔时间
		// 如果任务执行时间>间隔时间,那么下次任务执行需等此次任务执行后立即执行
		time += p;
	else
		// With任务,下次执行时间=任务执行时间+间隔时间
		time = triggerTime(-p);
}