线程池任务执行流程java
咱们从一个API开始接触Executor是如何处理任务队列的。ide
java.util.concurrent.Executor.execute(Runnable)this
Executes the given task sometime in the future. The task may execute in a new thread or in an existing pooled thread. If the task cannot be submitted for execution, either because this executor has been shutdown or because its capacity has been reached, the task is handled by the current RejectedExecutionHandler.spa
线程池中全部任务执行都依赖于此接口。这段话有如下几个意思:.net
- 任务可能在未来某个时刻被执行,有可能不是当即执行。为何这里有两个“可能”?继续往下面看。
- 任务可能在一个新的线程中执行或者线程池中存在的一个线程中执行。
- 任务没法被提交执行有如下两个缘由:线程池已经关闭或者线程池已经达到了容量限制。
- 全部失败的任务都将被“当前”的任务拒绝策略RejectedExecutionHandler 处理。
回答上面两个“可能“。任务可能被执行,那不可能的状况就是上面说的状况3;可能不是当即执行,是由于任务可能还在队列中排队,所以还在等待分配线程执行。了解完了字面上的问题,咱们再来看具体的实现。线程
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {
if (runState == RUNNING && workQueue.offer(command)) {
if (runState != RUNNING || poolSize == 0)
ensureQueuedTaskHandled(command);
}
else if (!addIfUnderMaximumPoolSize(command))
reject(command); // is shutdown or saturated
}
}
这一段代码看起来挺简单的,其实这就是线程池最重要的一部分,若是可以彻底理解这一块,线程池仍是挺容易的。整个执行流程是这样的:指针
- 若是任务command为空,则抛出空指针异常,返回。不然进行2。
- 若是当前线程池大小 大于或等于 核心线程池大小,进行4。不然进行3。
- 建立一个新工做队列(线程,参考上一节),成功直接返回,失败进行4。
- 若是线程池正在运行而且任务加入线程池队列成功,进行5,不然进行7。
- 若是线程池已经关闭或者线程池大小为0,进行6,不然直接返回。
- 若是线程池已经关闭则执行拒绝策略返回,不然启动一个新线程来进行执行任务,返回。
- 若是线程池大小 不大于 最大线程池数量,则启动新线程来进行执行,不然进行拒绝策略,结束。
文字描述步骤不够简单?下面图形详细表述了此过程。blog

老实说这个图比上面步骤更难以理解,那么从何入手呢。接口
流程的入口很简单,咱们就是要执行一个任务(Runnable command),那么它的结束点在哪或者有哪几个?队列
根据左边这个图咱们知道可能有如下几种出口:
(1)图中的P一、P7,咱们根据这条路径能够看到,仅仅是将任务加入任务队列(offer(command))了;
(2)图中的P3,这条路径不将任务加入任务队列,可是启动了一个新工做线程(Worker)进行扫尾操做,用户处理为空的任务队列;
(3)图中的P4,这条路径没有将任务加入任务队列,可是启动了一个新工做线程(Worker),而且工做现场的第一个任务就是当前任务;
(4)图中的P五、P6,这条路径没有将任务加入任务队列,也没有启动工做线程,仅仅是抛给了任务拒绝策略。P2是任务加入了任务队列却由于线程池已经关闭因而又从任务队列中删除,而且抛给了拒绝策略。
若是上面的解释还不清楚,能够去研究下面两段代码:
java.util.concurrent.ThreadPoolExecutor.addIfUnderCorePoolSize(Runnable)
java.util.concurrent.ThreadPoolExecutor.addIfUnderMaximumPoolSize(Runnable)
java.util.concurrent.ThreadPoolExecutor.ensureQueuedTaskHandled(Runnable)
那么何时一个任务被当即执行呢?
在线程池运行状态下,若是线程池大小 小于 核心线程池大小或者线程池已满(任务队列已满)而且线程池大小 小于 最大线程池大小(此时线程池大小 大于 核心线程池大小的),用程序描述为:
runState == RUNNING && ( poolSize < corePoolSize || poolSize < maxnumPoolSize && workQueue.isFull())
上面的条件就是一个任务可以被当即执行的条件。
有了execute的基础,咱们看看ExecutorService中的几个submit方法的实现。
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Object> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
public <T> Future<T> submit(Runnable task, T result) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task, result);
execute(ftask);
return ftask;
}
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
很简单,不是么?对于一个线程池来讲复杂的地方也就在execute方法的执行流程。在下一节中咱们来讨论下如何获取任务的执行结果,也就是Future类的使用和原理。