测试代码
public class TestThreadPool2 {
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.submit(newTask());
}
public static Runnable newTask() {
return new Runnable() {
@Override
public void run() {
System.out.println("run");
}
};
}
}
测试代码是通过ExecutorService提交将一个Runnable类型的任务提交给线程池来执行
历程
- 创建 run0:创建一个Runnable类型的任务,它的run方法只输出一个"run!",为了方便,我们把这个run方法称为run0
- submit 通过阅读源码发现最终调用的submit方法是由AbstractExecutorService类来实现的,这个submit方法最终由谁来实现,决定于线程池的构造方案,我构建了一个SingleThreadExecutor,这个线程池并没有对AbstractExecutorServicet类的submit方法进行重写。 接下来看看submit方法里面都干了些啥
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
- 首先不能提交空任务,否者抛个异常
- 然后通过newTaskFor方法将一个Runnable类型的task包装成一个RunnableFuture类型的ftask
- 然后调用executor方法执行ftask
- adapter newTaskFor(task, null)这一句的目的是将Runnable接口适配成一个返回值为null的FutureTask类型的任务。当然这都不重要,我们关心的是run0方法到哪里去了
// 1. 包装成一个FutureTask
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
return new FutureTask<T>(runnable, value);
}
// 2. 包装成一个Callable交给成员变量callable
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
//3. 包装成一个RunnableAdapter
public static <T> Callable<T> callable(Runnable task, T result) {
if (task == null)
throw new NullPointerException();
return new RunnableAdapter<T>(task, result);
}
//4. 交给了成员变量task,并交由call方法来执行
static final class RunnableAdapter<T> implements Callable<T> {
final Runnable task;
final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
public T call() {
task.run();
return result;
}
}
上面是看代码的顺序,经过一波操作,我们需要执行的run0方法最终交给了FutureTask的成员变量callable的call方法来执行,而FutureTask实现了Runnable接口的类。很明显,想要执行call方法肯定是通过FutureTask中的run方法来完成,以下是FutureTask的run方法实现,它确实执行了callable成员的call方法
public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call(); // 在这里执行了call方法
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} 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
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
现在已经很清楚了,想要执行run0方法,我们就需要在接下来的过程中调用ftask的run方法
4. excute
excute方法由ThreadPoolExcutor类来实现,至于为什么,这同样是在构造线程池的时候已经确定了的,我们来看execute方法
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
这个方法决定了怎么执行线程任务,我们同样不关心具体的逻辑,只关心run0的去想,很显然是通过addWorker方法来处理了
- addWorker
addWorker方法是线程池的核心方法,它负责控制线程什么情况可以提交,什么情况下执行,什么时候拒绝新线程的加入。这个方法很重要,想要学习线程池还需要了解这个方法,不过这里仍然不关心它的状态控制和调度逻辑,只关心run0的去想
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;
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) {
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
经过一顿操作之后,传入的task被包装成了一个Worker对象,然后执行worker对象中的成员线程thread。看到这里,基本上可以确定run0会随着worker的成员变量thread来执行了。我们一步步来看
// 1.包装成worker
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
这个过程将run0放到了worker的成员变量firstTask中,并以当前对象,也就是worker对象创建了一个成员线程thread,当thread线程启动的时候会执行传入的参数也就是worker对象的run方法。接下来看看worker的run方法
// 2.通过worker启动run0
public void 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);
Throwable thrown = null;
try {
//*********在这里执行了run0 *********
task.run();
//*********在这里执行了run0 *********
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
afterExecute(task, thrown);
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
从上面的代码可以看到,经过一波操作之后,worker调用了成员变量firstTask的run方法,这里的firstTask就是我们当初构造Worker 对象的时候传入的run0。
到这里已经看到,run0会随着addWorker方法中的线程启动而执行了。
总结
通过submit提交的任务,会经过适配、包装等一大波操作之后,最终通过Thread类来启动线程执行
- 适配成FutureTask类型任务,run0将由FutureTask的run方法来调用
- 使用FutureTask任务构造Worker对象,run0将由worker的run方法来启动
- 通过Worker对象构造一个Thread对象,run0将由thread的run方法来启动
在这个过程中发生了一个非常骚的操作
- run0这个方法被包装了3次,也就是说它的外层还有2个runnable接口
- run0这个方法曾经被适配成Callable接口,然后又被转换成Runnable接口