线程池ThreadPoolExecutor简单总结
线程池的构造函数,入参怎么理解?
线程池有几个概念,核心线程,核心线程数量,最大线程数,拒绝策略,任务队列。这些都是在初始化线程池的时候完成的(当然你也可以主动调用设置参数的方法)。
在说明这些参数的具体意义之前,先理解一个小场景,它和线程池的基本工作模式类似。
你有一家生产布偶玩具的小作坊,平时收到的订单量最多只需要你们一家四口就能完成,量少的时候甚至只需要一个人就能完成。但有时候订单量会骤增,你发现有些订单并不一定要最快速度完成,所以你会对这些订单做一个排队。当订单继续增加,就算有了排队机制,你仍然处理不过来。这时候你就想到了临时工,当然,招临时工这事儿你会外包给一个人力资源公司。在你的带领下,小作坊产品质量一直很好,客户不断的增加,订单越来越多,你得权衡这些订单能不能如期交付,毕竟不能如期交付的话,违约成本很高。所以,你就不得不拒绝一些订单,你还专门找了你的外甥来处理这些你拒绝的这些订单。
在看完上面这个场景之后,我们将其类比到线程池的参数:
corePoolSize 核心线程的数量。相当于你们家的一家四口,这个小作坊的核心成员。
maximumPoolSize 最大线程数量。相当于你的小作坊,最多能容纳多少人同时工作。
keepAliveTime 闲置线程存活的时间。相当于不干活的工人,在小作坊能待多久,等活儿的时间。
unit 闲置线程存活时间的单位。
workQueue 任务队列。相当于你对订单排序的队列,你可以按时间排序,或者其它规则。
threadFactory 线程工厂。相当于你外包给的那家人力公司,他为你提供工人。
handler 被拒绝的任务处理者。相当于你外甥,他会处理掉你拒绝的订单,有可能他再找人代工哦。
源码如下:
/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
* @param unit the time unit for the {@code keepAliveTime} argument
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method.
* @param threadFactory the factory to use when the executor
* creates a new thread
* @param handler the handler to use when execution is blocked
* because the thread bounds and queue capacities are reached
* @throws IllegalArgumentException if one of the following holds:<br>
* {@code corePoolSize < 0}<br>
* {@code keepAliveTime < 0}<br>
* {@code maximumPoolSize <= 0}<br>
* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException if {@code workQueue}
* or {@code threadFactory} or {@code handler} is null
*/
public ThreadPoolExecutor(int corePoolSize,//核心线程的数量
int maximumPoolSize,//最大线程数量(包含核心线程和非核心线程)
long keepAliveTime,//非核心线程执行完任务后,能闲置时间,
//没任务后能存活的时长,
// 超过则关闭线程
TimeUnit unit,//keepAliveTime的单位
BlockingQueue<Runnable> workQueue,//等待被执行的队列
ThreadFactory threadFactory,//创造线程的工厂
RejectedExecutionHandler handler) {//当任务被拒绝时的一个处理者
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
当任务提交到线程池,线程池会做什么?
先来看看execute的源码,干了什么。根据源码中的注释,执行了三个步骤:
1.如果正在执行的核心线程数量少于设定值(corePoolSize),则添加一个核心线程;
2.如果核心线程数量已经用完,则将当前任务添加到核心线程的队列中等待;(若添加成功,再检查一遍线程是否已不处于 RUNNING 状态,那么移除已经入队的这个任务,并且执行拒绝策略。因为第一次检查和添加的过程中可能线程池状态变化成非RUNNING状态)
3.如果将任务添加到队列失败,则添加一个非核心线程来执行该任务,若非核心线程添加也失败(比如添加的线程数超过最大限定的线程数量maximumPoolSize),则执行拒绝策略
代码:
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();
}
// 如果到这里,要么当前线程数大于等于核心线程数,要么刚刚 addWorker 失败
// 如果线程池处于 RUNNING 状态,把这个任务添加到任务队列 workQueue 中
if (isRunning(c) && workQueue.offer(command)) {
//添加成功后 再进行一次检查
int recheck = ctl.get();
//重新再检查线程是否处于running状态,如果没在running状态则移除任务
if (! isRunning(recheck) && remove(command))
reject(command);//没running,且从队列中移除,则调用拒绝策略
else if (workerCountOf(recheck) == 0)//判断线程池中还有没有线程
addWorker(null, false);
}
else if (!addWorker(command, false))//如果添加到队列失败,则开启新的非核心线程来执行
reject(command);//如果开启非核心线程失败,则执行拒绝策略
}
流程图:
线程什么时候被创建,什么时候被关闭?
1、核心线程创建时机:当一个任务被提交的线程池,而线程池中,没有空闲的线程可以用时创建(比如线程池已经有一个核心线程正在执行任务,核心线程数量设定为4,这时候来了一个新任务,则创建新线程,作为核心线程。)
2、核心线程关闭时机:一般情况下核心线程是不会被关闭的。除非allowCoreThreadTimeOut设置为true。
3、非核心线程的创建时机:当一个任务被提交到线程池,核心线程都已被占用,将该任务添加到队列也失败了,这时候就需要创建一个非核心线程来执行该任务。
4、非核心线程的关闭时机:当任务执行完成之后,且从任务队列里获取任务超时则关闭(这个超时时长就是非核心线程能闲置的时长getTask中的 如下代码);
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
一个线程在线程池中的生命过程,这个线程会执行哪些关键方法?
1、addWorker的时候会调用线程工厂的newThread(Worker)方法,
2、创建的这个线程会调用Worker的run()方法
3、执行runWorker(Worker)方法
4、runWorker()方法中,不断的循环调用getTask()从workerQueue中获取任务
5.1、如果成功获取任务,则执行该任务的run()方法,执行完run继续步骤4的循环
5.2、获取任务超时,或者返回为空则表示任务队列已经没有多余任务,进入下一步
6、processWorkerExit(w, completedAbruptly)执行工作人员的撤退流程
通常说线程池复用线程是如何实现的?
当我们创建线程后,调用线程的run方法,用一个循环,不断的从任务队列中获取任务,然后执行这些任务的run()方法。详细见上一条问答。