入口
调度任务的入口:java.util.concurrent.ScheduledThreadPoolExecutor#scheduleAtFixedRate
/**
* 调度执行
*
* @throws RejectedExecutionException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
* @throws IllegalArgumentException {@inheritDoc}
*/
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<Void> sft =
new ScheduledFutureTask<Void>(command,
null,
triggerTime(initialDelay, unit),
unit.toNanos(period));
RunnableScheduledFuture<Void> t = decorateTask(command, sft);
sft.outerTask = t;
//延迟执行
delayedExecute(t);
return t;
}
这个方法只是入口,没有干具体的事情。
继续看delayedExecute方法。
添加业务线程到队列
delayedExecute方法的主要步骤是:
1.先把业务线程添加到队列
2.创建调度线程(即Worker线程)
java.util.concurrent.ScheduledThreadPoolExecutor#delayedExecute
/**
* 主要步骤
* 1.先把业务线程添加到队列
* 2.创建调度线程(即Worker线程)
*
* ---
* Main execution method for delayed or periodic tasks. If pool
* is shut down, rejects the task. Otherwise adds task to queue
* and starts a thread, if necessary, to run it. (We cannot
* prestart the thread to run the task because the task (probably)
* shouldn't be run yet.) If the pool is shut down while the task
* is being added, cancel and remove it if required by state and
* run-after-shutdown parameters.
*
* @param task the task
*/
private void delayedExecute(RunnableScheduledFuture<?> task) {
if (isShutdown())
reject(task);
else {
//先把业务线程添加到队列
super.getQueue().add(task);
if (isShutdown() &&
!canRunInCurrentRunState(task.isPeriodic()) &&
remove(task))
task.cancel(false);
else
//创建调度线程(即Worker线程)
ensurePrestart();
}
}
以上代码,都是ScheduledThreadPoolExecutor类里的代码。
可以看到,ScheduledThreadPoolExecutor只是调用了ensurePrestart方法去创建调度线程,而从ensurePrestart方法开始,已经进入到了ThreadPoolExecutor类里面。
创建调度线程,添加调度线程到线程池,启动调度线程
ensurePrestart方法只是调用创建调度线程的方法addWorker:java.util.concurrent.ThreadPoolExecutor#ensurePrestart
/**
* 创建调度线程
*
* Same as prestartCoreThread except arranges that at least one
* thread is started even if corePoolSize is 0.
*/
void ensurePrestart() {
int wc = workerCountOf(ctl.get());
if (wc < corePoolSize)
//创建调度线程
addWorker(null, true);
else if (wc == 0)
addWorker(null, false);
}
addWorker方法是线程池ThreadPoolExecutor类的方法,这个是各种不同线程池的公共方法。
为什么ScheduledThreadPoolExecutor会调用ThreadPoolExecutor里的方法?
因为ScheduledThreadPoolExecutor继承了ThreadPoolExecutor。
public class ScheduledThreadPoolExecutor
extends ThreadPoolExecutor
implements ScheduledExecutorService {
java.util.concurrent.ThreadPoolExecutor#addWorker,主要步骤如下:
1.创建新的调度线程(即Worker线程)
2.并且添加新的调度线程到线程池
3.最后启动调度线程
/**
* 主要步骤
* 1.创建新的调度线程(即Worker线程)
* 2.并且添加新的调度线程到线程池
* 3.最后启动调度线程
*
* ---
* Checks if a new worker can be added with respect to current
* pool state and the given bound (either core or maximum). If so,
* the worker count is adjusted accordingly, and, if possible, a
* new worker is created and started, running firstTask as its
* first task. This method returns false if the pool is stopped or
* eligible to shut down. It also returns false if the thread
* factory fails to create a thread when asked. If the thread
* creation fails, either due to the thread factory returning
* null, or due to an exception (typically OutOfMemoryError in
* Thread.start()), we roll back cleanly.
*
* @param firstTask the task the new thread should run first (or
* null if none). Workers are created with an initial first task
* (in method execute()) to bypass queuing when there are fewer
* than corePoolSize threads (in which case we always start one),
* or when the queue is full (in which case we must bypass queue).
* Initially idle threads are usually created via
* prestartCoreThread or to replace other dying workers.
*
* @param core if true use corePoolSize as bound, else
* maximumPoolSize. (A boolean indicator is used here rather than a
* value to ensure reads of fresh values after checking other pool
* state).
* @return true if successful
*/
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
int wc = workerCountOf(c);
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
//线程池线程数量加1
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
}
boolean workerStarted = false;
boolean workerAdded = false;
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 rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
//添加新线程到线程池
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
//执行Worker线程
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
执行业务线程
执行业务线程的代码,其实也是使用ThreadPoolExecutor的公共代码:java.util.concurrent.ThreadPoolExecutor#runWorker。
本质其实就是让调度线程,去不停的执行业务线程。
之前的文章讲过,这里不细讲。可以参考:juejin.cn/post/712319…
调度线程池和一般线程池的唯一区别是,多了一个定时任务的功能,即会重复执行同一个任务。
那源码是如何实现的呢?主要步骤如下:
- 执行业务线程
- 因为业务线程被封装了,所以先执行的是封装业务线程
- 最后,才执行真正的业务线程
执行业务线程
执行业务线程,是在哪里触发?
前面说了,是在java.util.concurrent.ThreadPoolExecutor#runWorker方法触发。
因为业务线程被封装了,所以先执行的是封装业务线程
刚才的执行业务线程,并不是真正的业务线程,而是被封装的业务线程。
为什么要封装?就是多了一个定时执行任务的功能,本质其实就是重复执行任务。
被封装的业务线程,代码如下:java.util.concurrent.ScheduledThreadPoolExecutor.ScheduledFutureTask#run
/**
* 执行业务线程
*
* Overrides FutureTask version so as to reset/requeue if periodic.
*/
public void run() {
boolean periodic = isPeriodic();
if (!canRunInCurrentRunState(periodic))
cancel(false);
else if (!periodic) //判断是否重复执行
ScheduledFutureTask.super.run(); //非重复执行
else if (ScheduledFutureTask.super.runAndReset()) { //执行业务线程 //重复执行
setNextRunTime(); //设置下一次执行时间
//把同一个业务线程再次添加到队列:目的是为了重复执行
reExecutePeriodic(outerTask);
}
}
执行业务线程的代码是这一行:
ScheduledFutureTask.super.runAndReset()
执行真正的业务线程
在runAndReset方法里,会执行真正的业务线程:java.util.concurrent.FutureTask#runAndReset
/**
* 执行业务线程
*
* Executes the computation without setting its result, and then
* resets this future to initial state, failing to do so if the
* computation encounters an exception or is cancelled. This is
* designed for use with tasks that intrinsically execute more
* than once.
*
* @return {@code true} if successfully run and reset
*/
protected boolean runAndReset() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return false;
boolean ran = false;
int s = state;
try {
Callable<V> 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;
}
总结
ScheduledThreadPoolExecutor的本质是,基于线程池,实现定时任务。
定时任务的本质是,重复执行任务。