我们先来看看java线程池实现定时任务的一个简单实现demo
我们执行了一个 :延迟1秒钟开始执行并且每隔1秒钟执行一次的定时任务
scheduleAtFixedRate方法第一个参数传入一个lamda表达式的Runnable对象,输出当前执行该任务的线程的名称,和执行时距离开始时间的一个时间差
public class ScheduleTask {
public static void main(String[] args) {
//当前时间
long start = System.currentTimeMillis();
//创建一个单线程的线程池
ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
// 第二个参数为首次执行的延时时间,第三个参数为定时执行的间隔时间
service.scheduleAtFixedRate(() -> System.out.println(Thread.currentThread().getName() + " " + (System.currentTimeMillis() - start)),
1, 1, TimeUnit.SECONDS);
}
}
执行结果:
pool-1-thread-1 1022
pool-1-thread-1 2021
pool-1-thread-1 3021
pool-1-thread-1 4021
pool-1-thread-1 5021
pool-1-thread-1 6021
pool-1-thread-1 7021
pool-1-thread-1 8023
pool-1-thread-1 9020
pool-1-thread-1 10020
pool-1-thread-1 11020
pool-1-thread-1 12020
pool-1-thread-1 13020
pool-1-thread-1 14020
pool-1-thread-1 15020
下面我们根据上面这个demo的执行流程去源码里看看到底是怎么实现的
首先我们利用Executors.newSingleThreadScheduledExecutor()这个方法来生成了一个ScheduledExecutorService
ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
return new DelegatedScheduledExecutorService
(new ScheduledThreadPoolExecutor(1));
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
//调用父类ThreadPoolExecutor的构造方法
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
我们看到返回的对象是一个DelegatedScheduledExecutorService实例,它包装了一个ScheduledThreadPoolExecutor实例
而ScheduledThreadPoolExecutor构造方法调用了父类ThreadPoolExecutor的构造方法
ThreadPoolExecutor是实现线程池的一个核心类,如果对这个类不了解,建议先去了解一下这个类的实现原理,以后我也会写篇博客总结一下,本文不进行赘述
我们看到ScheduledThreadPoolExecutor构造方法中传入父类构造方法的BlockingQueue 参数是new DelayedWorkQueue();
这是它能够实现延迟和定时执行的关键,后面会讲述
现在我们来看看执行方法
long start = System.currentTimeMillis();
service.scheduleAtFixedRate(() -> System.out.println(Thread.currentThread().getName() + " " + (System.currentTimeMillis() - start)),//执行的Runnable任务
1, //延迟执行时间
1, //定时执行周期
TimeUnit.SECONDS); //单位秒
}
调用ScheduledThreadPoolExecutor.scheduleAtFixedRate方法
public ScheduledFuture> scheduleAtFixedRate(Runnable command,
long initialDelay,
long period,
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
if (period <= 0L)
throw new IllegalArgumentException();
//生成一个task
ScheduledFutureTask sft =
new ScheduledFutureTask(command,
null,
triggerTime(initialDelay, unit),//开始执行任务延迟时间
unit.toNanos(period),//定时任务执行间隔
sequencer.getAndIncrement());
//装饰一下,这里其实就是返回了sft本身
RunnableScheduledFuture t = decorateTask(command, sft);
sft.outerTask = t;
//执行
delayedExecute(t);
return t;
}
ScheduledFutureTask(Runnable r, V result, long triggerTime,
long sequenceNumber) {
super(r, result);
this.time = triggerTime;//开始执行任务的时间 纳秒
this.period = 0;//执行定时任务的周期 纳秒
this.sequenceNumber = sequenceNumber;
}
延迟执行方法
private void delayedExecute(RunnableScheduledFuture> task) {
if (isShutdown())
reject(task);
else {
//将任务放入构造方法里创建的DelayedWorkQueue中,放入的实际是个ScheduledFutureTask实例
super.getQueue().add(task);
if (!canRunInCurrentRunState(task) && remove(task))
task.cancel(false);
else
//执行预处理
ensurePrestart();
}
}
void ensurePrestart() {
//计算当前线程池中的线程数量,因为我们是第一次调用这里,还没创建线程,这里wc == 0
int wc = workerCountOf(ctl.get());
//我们是单线程池,这里corePoolSize == 1
if (wc < corePoolSize)
//简单理解为创建了一个Worker线程来对DelayedWorkQueue中的任务进行处理
addWorker(null, true);
else if (wc == 0)
addWorker(null, false);
}
调用父类的addWorker方法,创建工作线程
private boolean addWorker(Runnable firstTask, boolean core) {
//省略一些校验
...............
boolean workerStarted = false;
boolean workerAdded = false;
//工作线程对象,实现了Runnable接口
Worker w = null;
try {
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
int c = ctl.get();
if (isRunning(c) ||
(runStateLessThan(c, STOP) && firstTask == null)) {
if (t.getState() != Thread.State.NEW)
throw new IllegalThreadStateException();
//加入到Worker集合中
workers.add(w);
workerAdded = true;
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
//启动工作线程
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
Worker的run方法里调用了runWorker方法,参数为this
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
//从任务队列中获取任务
while (task != null || (task = getTask()) != null) {
w.lock();
// If pool is stopping, ensure thread is interrupted;
// if not, ensure thread is not interrupted. This
// requires a recheck in second case to deal with
// shutdownNow race while clearing interrupt
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
beforeExecute(wt, task);
try {
//执行之前放入延迟队列的ScheduledFutureTask的run方法
task.run();
afterExecute(task, null);
} catch (Throwable ex) {
afterExecute(task, ex);
throw ex;
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
这里while循环通过getTask获取需要执行的任务
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
int c = ctl.get();
// Check if queue empty only if necessary.
if (runStateAtLeast(c, SHUTDOWN)
&& (runStateAtLeast(c, STOP) || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
//我们只有一个worker,所以这里wc == 1
int wc = workerCountOf(c);
// Are workers subject to culling?
//这里没有设置allowCoreThreadTimeOut并且wc > corePoolSize为false,所以timed == false
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
//这里从workQueue(初始化时传入的DelayedWorkQueue实例)获取任务会走take方法,
//因为前面delayedExecute方法里加入了一个task,所以workQueue里已经有一个任务了
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
来看看DelayedWorkQueue.take方法的实现
public RunnableScheduledFuture> take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
for (;;) {
RunnableScheduledFuture> first = queue[0];
if (first == null)
available.await();
else {
//计算还需要等待的时间
long delay = first.getDelay(NANOSECONDS);
if (delay <= 0L)
//结束循环返回task
return finishPoll(first);
first = null; // don't retain ref while waiting
if (leader != null)
available.await();
else {
Thread thisThread = Thread.currentThread();
leader = thisThread;
try {
//等待delay纳秒的时间
available.awaitNanos(delay);
} finally {
if (leader == thisThread)
leader = null;
}
}
}
}
} finally {
if (leader == null && queue[0] != null)
available.signal();
lock.unlock();
}
}
public long getDelay(TimeUnit unit) {
return unit.convert(time - System.nanoTime(), NANOSECONDS);
}
可以看到任务的延迟和定时执行是通过available.awaitNanos(delay)方法实现的
而这个delay是通过任务的getDelay(NANOSECONDS)方法获取的
它是用task(ScheduledFutureTask)任务初始化时传入的delay时间 - 系统当前时间获得的
这就是为什么任务可以实现延迟执行
最后我们发现take方法是调用finishPoll(first)返回的task
这里主要是因为DelayedWorkQueue里面使用二叉堆来维护task列表,所以取出头元素后会调整一下后面元素的位置,感兴趣的同学可以去看一下。对于有数据结构算法基础的同学应该能很容易理解,如果没有的话建议最好系统学习一下,毕竟也是很重要的一块技能,推荐去看下《算法 第四版》这本书。
好了,回到前面runWorker的task.run()方法,这里的task是ScheduledFutureTask实例,所以直接看ScheduledFutureTask.run方法
public void run() {
//检查状态
if (!canRunInCurrentRunState(this))
cancel(false);
//如果不是定时周期执行任务走这里
else if (!isPeriodic())
super.run();
//其他走这里
else if (super.runAndReset()) {
setNextRunTime();
reExecutePeriodic(outerTask);
}
}
这里我们会走runAndReset方法
protected boolean runAndReset() {
if (state != NEW ||
!RUNNER.compareAndSet(this, null, Thread.currentThread()))
return false;
boolean ran = false;
int s = state;
try {
Callable c = callable;
if (c != null && s == NEW) {
try {
//方法调用
c.call(); // don't set result
ran = true;
} catch (Throwable ex) {
setException(ex);
}
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
return ran && s == NEW;
}
c.call(),这里c是一个Callable对象,它是在前面ScheduledFutureTask初始化时包装的一个对象,实际实现是RunnableAdapter
可以看到call方法就是调用task的run方法,而这里的task就是我们demo中传入的lamda表达式对象
private static final class RunnableAdapter implements Callable {
private final Runnable task;
private final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
public T call() {
task.run();
return result;
}
public String toString() {
return super.toString() + "[Wrapped task = " + task + "]";
}
}
到这里,就完成了第一次的task执行,那么后序该怎么实现定时任务呢?
其实相信这里大家也能猜出个大概了
1.首先之前我们获取task的时候从任务队列中取出了一个任务,这时任务队列已经是空的了,所以后面肯定会有一个地方将task再次放入任务队列
2.我们前面分析DelayedWorkQueue.take方法时,了解到代码通过task.getDelay方法来获取等待时间,而task.getDelay通过保存的成员变量time-当前系统时间获得,如果我们不重置这个time属性,那么下次去调用DelayedWorkQueue.take时获取的时间肯定是负的,任务会立马执行,也就起不到定时任务的效果了
回到runAndReset执行完后,会进入setNextRunTime方法,这里重置了time,将time加上了定时任务时间间隔period
private void setNextRunTime() {
long p = period;
if (p > 0)
time += p;
else
time = triggerTime(-p);
}
然后是reExecutePeriodic,这里将task再次加入任务队列
void reExecutePeriodic(RunnableScheduledFuture> task) {
if (canRunInCurrentRunState(task)) {
super.getQueue().add(task);
if (canRunInCurrentRunState(task) || !remove(task)) {
ensurePrestart();
return;
}
}
task.cancel(false);
}
然后就是一直在runWorker的while循环里获取task和执行task
整个流程就差不多是这样,主要是利用DelayedWorkQueue和ScheduledFutureTask来实现延迟执行和定时任务的效果