测试代码
public class Test {
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方法到哪里去了
- 包装成一个FutureTask
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) { return new FutureTask<T>(runnable, value); }
- 包装成一个Callable交给成员变量callable
public FutureTask(Runnable runnable, V result) { this.callable = Executors.callable(runnable, result); this.state = NEW; // ensure visibility of callable }
- 包装成一个RunnableAdapter
public static <T> Callable<T> callable(Runnable task, T result) { if (task == null) throw new NullPointerException(); return new RunnableAdapter<T>(task, result); }
- 交给了成员变量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方法
-
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);// 包装成worker /// 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来执行了。我们一步步来看
- 包装成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(); // } 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接口