线程池的提交流程
一、必备知识
需要知道线程池的核心参数,以及具体作用
int corePoolSize, 核心线程数
int maximumPoolSize, 最大线程数
long keepAliveTime, 最大存活时间
TimeUnit unit, 时间单位
BlockingQueue<Runnable> workQueue, 任务队列
ThreadFactory threadFactory, 线程工厂
RejectedExecutionHandler handler 拒绝策略
// 线程池的构造函数可以体现
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
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.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
二、源码分析前需要知道的概念
首先两个问题:
1、如何控制线程池的状态?
2、如何控制线程数?
在jdk线程池源码中使用了一个变量ctl来进行控制,
// 用来控制线程池状态、数量
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
AtomicInteger 内部是一个int型value值进行保存,每次的获取状态信息、获取线程数信息都是取这个数;
那如何存储?
我们知道int型占4个字节,32个位,jdk把这32个位的前3位用来保存线程池的状态,后29位保存线程池的数量
源码如下:
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// 29
private static final int COUNT_BITS = Integer.SIZE - 3;
// 容量 (1 << 29) - 1 == 536870911;
// 二进制位图:0001 1111 1111 1111 1111 1111 1111 1111
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
/**
* 1110 0000 0000 0000 0000 0000 0000 0000
* 接受新任务并处理排队的任务
*/
private static final int RUNNING = -1 << COUNT_BITS;
/**
* 0000 0000 0000 0000 0000 0000 0000 0000
* 不接受新任务,而是处理排队的任务
*/
private static final int SHUTDOWN = 0 << COUNT_BITS;
/**
* 0010 0000 0000 0000 0000 0000 0000 0000
* 不接受新任务,不处理排队的任务,中断正在进行的任务
*/
private static final int STOP = 1 << COUNT_BITS;
/**
* 0100 0000 0000 0000 0000 0000 0000 0000
* 所有任务都已终止,workerCount 为零,转换到状态 TIDYING 的线程将运行 terminate() 钩子方法
*/
private static final int TIDYING = 2 << COUNT_BITS;
/**
* 0110 0000 0000 0000 0000 0000 0000 0000
* 已终止、已完成
*/
private static final int TERMINATED = 3 << COUNT_BITS;
状态对应高位结果如下:
线程池状态 | 高3位 | 状态解释 |
---|---|---|
RUNNING | 111 | 接受新任务并处理排队的任务 |
SHUTDOWN | 000 | 不接受新任务,而是处理排队的任务 |
STOP | 001 | 不接受新任务,不处理排队的任务,中断正在进行的任务 |
TIDYING | 010 | 所有任务都已终止,workerCount 为零,转换到状态 TIDYING 的线程将运行 terminate() 钩子方法 |
TERMINATED | 011 | 已终止、已完成 |
位计算方法 | 作用 | 方法体内容 |
---|---|---|
runStateOf(int c) | 主要获取高3位信息 | c & ~CAPACITY |
workerCountOf(int c) | 获取低29位信息 | c & CAPACITY |
ctlOf(int rs, int wc) | 初始化ctl | rs |
三、核心类
拒绝策略可以自己看看源码;主要是Worker类;
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
/** 维护的线程,从线程工厂中获取的*/
final Thread thread;
/** 执行的第一个任务,也是你execute(Runnable command)的任务,该任务执行完毕后会从队列获取 */
Runnable firstTask;
Worker(Runnable firstTask) {
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
/** worker启动后,会调用ThreadPoolExecutor的runWorker方法,启动线程去工作 */
public void run() {
runWorker(this);
}
}
四、提交流程源码分析 – 只摘取主要代码
4.1 任务提交
public void execute(Runnable command) {
/** 线程池new出来的时候,ctl被init,
ctl == ctlOf(RUNNING, 0) == RUNNING | 0
== 1110 0000 0000 0000 0000 0000 0000 0000
所以状态高位111,为运行态,后面为0,线程数为0
*/
int c = ctl.get();
// 取ctl低位为0,小于核心线程数,所以创建一个Worker加入线程集合;
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true)) // addWorker见下面分析
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见下面分析
// 如果线程数为0,则创建一个新线程加入线程集合
addWorker(null, false);
}
else if (!addWorker(command, false)) //addWorker见下面分析
// 尝试启动一个新线程去启动失败,调用拒绝策略处理任务;
reject(command);
}
1:当有新的任务添加时,如果线程数小于核心线程数,直接通过线程工厂获取线程,封装成Worker启动线程;
2:当前程数超过核心线程数,先判断线程池状态并将任务加入队列,如果线程现在是非运行态,则移除队列任务,调用拒绝策略来处理;
3:当前线程数超过核心线程数,先判断线程池状态并将任务加入队列,如果加入队列失败,则启动一个线程尝试处理,如果失败则调用拒绝策略;
4.2 添加线程到线程集合 --HashSet workers
/**
core 是否核心线程
*/
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// 状态大于等于SHUTDOWN,可能STOP、TIDYING、TERMINATED,则不接受任务
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))
// 线程数自增加1跳出循环
break retry;
c = ctl.get();
if (runStateOf(c) != rs)
// 线程池状态变化重试
continue retry;
// 自增加1失败,再次循环
}
}
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
w = new Worker(firstTask); // 将提交的任务封装成Worker,并从线程工厂内获取线程赋值给Worker,见下文
final Thread t = w.thread; // Worker的线程
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 获取运行状态
int rs = runStateOf(ctl.get());
// 线程是运行态或者关闭态但是任务为null
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // 线程已经激活报错
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;
}
1:首先判断状态大于等于SHUTDOWN,可能STOP、TIDYING、TERMINATED,则不接受任务;
2:无线循环修改ctl自增1,代表线程数要加1;
3:将Runnable firstTask, 封装成Worker(线程在构造中通过线程工厂获取的), 放入Workers集合;
4:启动Worker中的线程,
4.3 Worker分析
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
// Worker维护的线程
final Thread thread;
/**
执行的第一个任务,也是你execute(Runnable command)的任务,该任务执行完毕后会从队列获取 */
Runnable firstTask;
Worker(Runnable firstTask) {
this.firstTask = firstTask;
// 从线程工厂中获取线程,你可以自己设置线程工厂
this.thread = getThreadFactory().newThread(this); // this交给线程工厂,线程工厂把Worker交给了Thread,因为Worker实现了Runnable,看下文,4.6
}
public void run() {
runWorker(this); // Worker中维护的线程启动后会调用此方法;
}
}
1:调用ThreadPoolExecutor的runWorker方法启动线程;
4.4 runWorker获取队列任务分析
final void runWorker(Worker w) {
// 获取Worker中的线程和任务
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();
try {
// 类似于拦截器,执行前拦截,这是个protected方法,用于扩展,你可以自己实现
beforeExecute(wt, task);
Throwable thrown = null;
try {
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);
// 类似于拦截器,执行后拦截,这是个protected方法,用于扩展,你可以自己实现
}
} finally {
// 当前任务执行完毕赋值为空,等获取到新任务继续赋值
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
// 队列为空获取不到任务跳出While,以及线程处理task出错,线程退出;
processWorkerExit(w, completedAbruptly);
}
}
1:首先获取Worker中的firstTask,并执行;
2:无线循环从队列中获取任务并执行,如果任务获取为空,则跳出循环,当前线程执行完毕;
4.5 getTask 从队列获取任务分析
private Runnable getTask() {
// 获取任务是否超时
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// SHUTDOWN 后续状态或者工作队列为空,则把线程减少至0;
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// 线程数大于核心线程数
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
// 线程数超过最大线程数,并且上次拉取任务超时了,你设置的最大存活时间
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
// 将线程数减1,返回任务为空
return null;
continue;
}
try {
// 根据你设置的最长等待时间来获取任务,
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)
workQueue.take();
if (r != null)
// 将任务返回
return r;
// 超过最长存活时间
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
1:如果线程池状态大于SHUTDOWN或者队列isEmpty,则返回空;
2:如果线程数大于核心线程数并且获取任务超时,则线程数减1,返回空;
3:从队列中获取任务,传入等待时间,不穿时间参数则长时间获取,就是Park;
4.6 Executors.defaultThreadFactory() 线程工厂
前面有Worker中维护了task和线程,而线程是从线程工厂中获得;但是Worker的Run是怎么启动的呢?
// 默认的线程工厂
public static ThreadFactory defaultThreadFactory() {
return new DefaultThreadFactory();
}
static class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
public Thread newThread(Runnable r) {
// 主要看这里的r, r是Worker,而Worker的Run是线程启动调用的;
Thread t = new Thread(group, r, // 这里r传入了线程中,所以线程启动的时候这里会执行Worker的run方法;
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
至此就分析完毕了;
总结:
1:当有新的任务添加时,如果线程数小于核心线程数,直接通过线程工厂获取线程,封装成Worker启动线程;
2:当前程数超过核心线程数,先判断线程池状态并将任务加入队列,如果线程现在是非运行态,则移除队列任务,调用拒绝策略来处理;
3:当前线程数超过核心线程数,先判断线程池状态并将任务加入队列,如果加入队列失败,则启动一个线程尝试处理,如果失败则调用拒绝策略;
4:启动新线程如果线程池状态是RUNNING并且线程数或者超过最大线程数都会启动新线程失败;
5:启动得新线程会先执行firsttask,执行完毕之后去队列获取新任务,获取新任务的时候如果第一次获取任务超时,并且线程数大于核心线程数则返回任务为空,进行线程退出,线程数减1;
结尾
keepAliveTime只对超过核心线程数的线程有作用;
能力有限,有错误的地方多谢指正,有错误既更新;