Java线程池
线程池是用来管理线程生命周期的一个对象池,通过使用线程池,可以让开发人员不过于关注线程的创建、销毁等过程。并且通过使用线程池,合理的设置核心线程数,最大线程池等参数,可以提高系统的性能,避免出现一些例如OOM的问题。
以上就是主线程中提交任务后的执行顺序。
首先看核心线程池是否已满,如果已经满了,就把任务放入到阻塞队列中,否则就创建新的线程,执行任务。
然后看阻塞队列是否已满,如果未满,就将任务放入阻塞队列,如果已满,就放入到最大线程池中。
再看最大线程池是否已满,如果已满就执行拒绝策略。未满就在最大线程池中创建线程执行任务。
在这个任务提交过程中深入考虑可能会遇到这几个问题。
- 主线程是通过execute来提交任务的吗?还有其他方式吗?
- 核心线程池中有存放线程对象吗?还是每次有任务到核心线程池的时候,再去创建线程?
- 任务放入到阻塞队列之后,线程池是在什么时机去获取的任务?
- 任务流转到了最大线程池这里,最大线程池会自己创建线程执行任务?如果这样,中间为什么要有一个阻塞队列的步骤?
下面我们就一一看一下这几个问题。
1. 主线程是通过execute来提交任务的吗?还有其他方式吗?
线程池可以通过execute,submit来提交任务。
但是最终都是通过调用execute来执行提交任务。
- 当核心线程数还未达到corePoolSize的时候,会尝试新建一个线程来执行这个任务。调用addWorker方法会自动检查运行状态和工作者数量,确保任务交给新线程出错时会有异常返回。
- 如果一个任务可以正常排队等待被执行,我们依旧需要二次检查我们是否可以新建一个线程(因为存在的一个线程可能在最后一次检查之后死掉了)或者线程池是否在进入这个方法之前已经关掉了。所以我们需要再一次检查在任务入队停止的时候是否需要回滚,或者新建一个线程。
- 如果任务不能排队,我们试图创建一个新的线程,如果这也失败了,我们要知道线程池可能被关掉了,或者线程池饱和了,这个时候就需要执行拒绝策略。
//接口的实现
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* 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