线程池ThreadPoolExecutor执行原理解析
在日常开发中,我们经常会使用线程池来进行线程的管理,他能很好的帮助我们管理线程,比如线程复用以节约我们重复创建线程的开支,我们今天就来看下线程池的基本原理。
线程池的构造
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.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
首先,我们看下线程池的构造方法,他由七个参数组成;具体的参数可以看注释;
线程池原理
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
ctl包含两部分信息:
workerCount 当前有效的线程数
runState 当前线程池的状态 Running、Shutdown、Stop、Tidying、Terminate
ctl具体可以看https://www.cnblogs.com/moonfair/p/13477974.html 这个博客
*/
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);
// 如果线程池正常工作,则判断工作线程数量是否为0
else if (workerCountOf(recheck) == 0)
// 因为之前已经将任务添加到了队列,所以这里添加一个空的工作线程,保证可以执行刚才队列中的任务
addWorker(null, false);
}
// 到这里则是工作队列offer不进去了,已经满了,则尝试创建新的工作线程执行任务
else if (!addWorker(command, false))
// 如果最大线程也满了,创建不了新的工作线程,则执行拒绝策略
reject(command);
}
上面是线程池的execute方法,在这方法中可以看见他的具体流程;我们可以从下图很清楚的看到流程;
接下来我们看一下addWorker()是怎么运行的?
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
// 如果当前线程池是非工作状态且(当前任务是null 或者 工作队列为空 或者 线程池状态是SHUTDOWN)
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;
// 利用cas将当前工作线程数量+1,若增加成功则跳出第一层循环
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 {
// 将当前任务封装成Worker工作线程对象
w = new Worker(firstTask);
// 这里的线程是ThreadFactory生成的新的线程
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;
// 将标识置为true
workerAdded = true;
}
} finally {
// 释放锁
mainLock.unlock();
}
// 如果标识为true,则启动worker线程,运行worker的run方法
if (workerAdded) {
t.start();
// 将标志置为true
workerStarted = true;
}
}
} finally {
// 如果工作线程启动失败,将当前工作线程置为失败并移除
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
可以清楚的看见这一步主要就是创建了一个工作线程对象(Worker)包裹了当前的任务,然后执行了Worker对象的run方法;
public void run() {
runWorker(this);
}
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
// 获取到当前的任务,然后将工作线程的置为Null, 初始化一些信息(这里没有理解)
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
// 死循环,判断当前task是否为空,如果是则调用getTask从队列中获取一个
// getTask有很多校验与超时的配置,这里不详解,可看下源码;总之是为了从队列中poll或take一个待执行任务并移除;
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 {
// 线程的真正执行
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置空,然后将锁释放
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
// 可能因为在队列中获取不到任务了,所以退出了while循环,这里会进行工作线程的退出;
processWorkerExit(w, completedAbruptly);
}
}
看完runWorker的完整流程,可以知道线程池是如何完成线程复用的,他在完成了当前任务后,会继续从阻塞队列中获取下一个待执行任务;如此反复一直到获取不到任务才会停止;在getTask中,设置了超时时间,当一段时间(这个时间就是线程池七个参数中的keepAliveTime) 获取不到任务也会结束当前线程;
这种方式可以解决创建大量创建线程的时间;也能更好的管理线程的生命周期;
时在中春,阳和方起