目录
1.ScheduleThreadPoolExecutor介绍
2.ScheduleThreadPoolExecutor应用
3. ScheduleThreadPoolExecutor源码
3.3 scheduleAtFixedRate和scheduleWithFixedDelay方法
1.ScheduleThreadPoolExecutor介绍
ScheduleThreadPoolExecutor支持延迟执行以及周期性执行的功能,
延迟执行:基于延迟队列实现
周期性执行:当任务执行完之后,再次设置好延迟时间,将任务重新扔到延迟队列中执行
2.ScheduleThreadPoolExecutor应用
定时任务线程池的有参构造,调用了父类 ThreadPoolExecutor 的构造方法,
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue(), threadFactory, handler);
}
它的构造方法最多可以设置三个参数
- 核心线程数
- 线程工厂
- 拒绝策略
它默认使用延迟队列 DelayedWorkQueue,这是一个无界队列,既然是无界队列,那么就轮不到非核心线程来执行,所以不需要设置最大线程数,临时线程的存活时间,unit等等,,,
代码示例
public class ScheduleThreadPoolExecutorDemo {
public static void main(String[] args) {
ScheduledThreadPoolExecutor scheduleExecutor = new ScheduledThreadPoolExecutor(
10,
new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setName("myThread");
return thread;
}
},
new ThreadPoolExecutor.AbortPolicy()
);
// ---------- 分别测试四种方式 ------------------------
// 和线程池的execute没啥区别
scheduleExecutor.execute(() -> {
System.out.println("execute");
});
// 指定延迟时间执行
System.out.println(System.currentTimeMillis());
scheduleExecutor.schedule(() -> {
System.out.println("schedule");
System.out.println(System.currentTimeMillis());
}, 2, TimeUnit.SECONDS);
// 指定任务第一次执行的延迟时间,并且设置第一次之后的周期执行时间,
// 周期时间是在任务开始时就计算,(并且是在原有的时间上做累加,不关注任务的执行时长)
// 第一次 2s,之后每次都是 4s
System.out.println(System.currentTimeMillis());
scheduleExecutor.scheduleAtFixedRate(() -> {
System.out.println("scheduleAtFixedRate");
System.out.println(System.currentTimeMillis());
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, 2, 3, TimeUnit.SECONDS);
// 指定第一次的延迟时间,并且设置第一次之后的周期执行时间,
// 周期时间是在任务结束后再计算下次的延迟时间
// 第一次 2s,之后每次都是 7s
System.out.println(System.currentTimeMillis());
scheduleExecutor.scheduleWithFixedDelay(() -> {
System.out.println("scheduleWithFixedDelay");
System.out.println(System.currentTimeMillis());
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, 2, 3, TimeUnit.SECONDS);
}
}
3. ScheduleThreadPoolExecutor源码
3.1 核心属性
// 这里是针对任务取消时的一些业务判断会用到的标记
// 周期执行任务,shutdown之后,默认不执行
private volatile boolean continueExistingPeriodicTasksAfterShutdown;
// 延迟执行任务,shutdown之后,默认执行
private volatile boolean executeExistingDelayedTasksAfterShutdown = true;
private volatile boolean removeOnCancel = false;
// 计数器,如果两个任务的执行时间节点一模一样,根据这个序列来判断谁先执行
private static final AtomicLong sequencer = new AtomicLong();
// 获取当前系统时间的毫秒值
final long now() {
return System.nanoTime();
}
// 内部类,核心类之一
private class ScheduledFutureTask<V>
extends FutureTask<V> implements RunnableScheduledFuture<V> {
// 全局唯一的序列,如果两个任务时间一样,基于当前属性判断谁先执行
private final long sequenceNumber;
// 任务执行的时间
private long time;
/**
* period == 0:执行一次的延迟任务
* period > 0:代表是scheduleAtFixedRate
* period < 0:代表是scheduleWithFixedDelay
*/
private final long period;
// 周期性执行时,需要将任务重新扔回阻塞队列,基础当前属性拿到任务,方便扔回阻塞队列
RunnableScheduledFuture<V> outerTask = this;
/**
* 构建schedule方法的任务
*/
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();
}
}
// 内部类,核心类之一
static class DelayedWorkQueue extends AbstractQueue<Runnable>
implements BlockingQueue<Runnable> {
}
3.2 schedule方法
execute 方法也是执行schedule方法,只不过传入的delay==0,
该方法就是将任务和延迟时间封装到一起,并且将任务扔到阻塞队列中,再去创建工作线程去take阻塞队列,
// command: 任务
// delay: 延迟时间
// unit:延迟时间的单位
public ScheduledFuture<?> schedule(Runnable command,
long delay,
TimeUnit unit) {
// 非空判断
if (command == null || unit == null)
throw new NullPointerException();
// triggerTime: 计算延迟时间,最终返回的是当前系统时间 + 延迟时间
// ScheduledFutureTask有参构造:将任务以及延迟时间封装到一起,并设置执行方式
// decorateTask:可以动态修改任务的一个扩展口
RunnableScheduledFuture<?> t = decorateTask(command,
new ScheduledFutureTask<Void>(command, null,
triggerTime(delay, unit)));
// 执行延迟任务
delayedExecute(t);
// 返回futureTask
return t;
}
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) {
super(r, result);
// 任务要执行的时间
this.time = ns;
// 执行一次的延迟任务
this.period = 0;
// 基于AtmoicLong生成的唯一序列
this.sequenceNumber = sequencer.getAndIncrement();
}
/**
* 执行延迟任务的操作
*
*/
private void delayedExecute(RunnableScheduledFuture<?> task) {
// 判断线程池状态
if (isShutdown())
// shutdown,执行拒绝策略
reject(task);
else {
// 状态正常RUNNING
// 将任务扔到阻塞队列
super.getQueue().add(task);
// 再次检查状态,如果不正常,就将任务从队列中移除
if (isShutdown() &&
!canRunInCurrentRunState(task.isPeriodic()) &&
remove(task))
task.cancel(false);
else
// 状态正常,任务可以执行
ensurePrestart();
}
}
/**
* 根据状态以及任务执行方式,判断任务是否可以执行
* 线程池shutDown之后:
* <p>
* 周期任务: 默认不可以执行
* 执行一次的延迟任务: 默认可以执行
* </p>
*
*/
boolean canRunInCurrentRunState(boolean periodic) {
return isRunningOrShutdown(periodic ?
continueExistingPeriodicTasksAfterShutdown :
executeExistingDelayedTasksAfterShutdown);
}
final boolean isRunningOrShutdown(boolean shutdownOK) {
int rs = runStateOf(ctl.get());
return rs == RUNNING || (rs == SHUTDOWN && shutdownOK);
}
/**
* 从队列中移除任务
*
*/
public boolean remove(Runnable task) {
// 从队列中移除任务,队列自身的remove加了锁
boolean removed = workQueue.remove(task);
// 再次查看线程池状态,查看是否需要进入 terminated
tryTerminate();
return removed;
}
cancel 方法,执行了 futureTask 中的方法,需要先了解 futureTask 源码再看,,
3.3 scheduleAtFixedRate和scheduleWithFixedDelay方法
这两个方法几乎一样,看一个就行,
// command: 任务
// initialDelay: 第一次执行的延迟时间
// period: 任务的周期执行时间
// unit: 时间单位
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
long initialDelay,
long period,
TimeUnit unit) {
// 非空校验
if (command == null || unit == null)
throw new NullPointerException();
// 周期时间不允许小于0
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;
// 执行延迟任务的操作,和schedule一样
delayedExecute(t);
// 返回futureTask
return t;
}
/**
* 周期执行任务的有参构造
*
*/
ScheduledFutureTask(Runnable r, V result, long ns, long period) {
super(r, result);
this.time = ns;
this.period = period;
this.sequenceNumber = sequencer.getAndIncrement();
}
延迟任务以及周期任务在执行时,都会调用当前任务的run方法,,
3.3.1 run方法
public void run() {
// periodic == false:一次性延迟任务
// periodic == true:周期任务
boolean periodic = isPeriodic();
// 根据状态判断能否执行任务
// 周期执行:false 一次性任务:true
if (!canRunInCurrentRunState(periodic))
// futureTask相关
cancel(false);
// 判断是周期执行还是一次性任务
else if (!periodic)
// 一次性任务,让工作线程直接执行command
ScheduledFutureTask.super.run();
else if (ScheduledFutureTask.super.runAndReset()) {
// 周期性任务
// 设置下次任务执行的时间
setNextRunTime();
// 将任务重新扔回线程池做处理
reExecutePeriodic(outerTask);
}
}
private void setNextRunTime() {
// 拿到period值,正数:At,负数:With
long p = period;
if (p > 0)
// At
// 拿着上一次的执行时间,直接追加上周期时间
time += p;
else
// With
// 拿到当前系统时间,追加上延迟时间
time = triggerTime(-p);
}
void reExecutePeriodic(RunnableScheduledFuture<?> task) {
// 根据线程池状态判断,是否可以执行
if (canRunInCurrentRunState(true)) {
// 将任务加入延迟队列
super.getQueue().add(task);
// DCL,判断线程池状态
if (!canRunInCurrentRunState(true) && remove(task))
task.cancel(false);
else
// 添加工作线程执行任务
ensurePrestart();
}
}