线程池流程图
个人认为,别人画的流程图是不容易记忆或者容易忘记的东西;这里就不提供不太有价值的流程图了。个人建议花时间看一下源码设计,真正理解了代码在程序中是如何运转的,各个阶段各个分支是怎么处理的,自己再根据自己的理解画出自己认为正确的流程图,相信这样的流程图画出来之后更容易记忆也不容易忘记。
线程池参数
int corePoolSize: // 核心线程数,核心工作线程的数量
int maximumPoolSize: // 最大线程数,包括核心线程数和非核心线程数
long keepAliveTime: // 非核心线程空闲存活时间
TimeUnit unit: // 非核心线程空闲存活时间单位,配合keepAliveTime使用
BlockingQueue<Runnable> workQueue: // 阻塞队列,用于存放工作线程未来得及处理的待执行任务
ThreadFactory threadFactory: // 线程工厂,用于生成worker对象中真正处理任务的线程。
RejectedExecutionHandler handler: // 拒绝策略,当工作线程数达到了最大线程数且队列中的待执行任务已满,会触发拒绝策略
线程池的各个参数,在源码中是如何使用的,可以带着问题去源码中寻找答案。
源码分析
线程池执行任务的入口:ThreadPoolExecutor.execute(Runnable command)(submit方法提交上来的callable实现类,最终被封装成了RunnableFuture的实现类,同时实现了Runable接口和Future接口,最终也是执行的execute方法)。先看线程池全局层面的执行逻辑。
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
// 如果工作线程数小于核心线程数,直接addworker(command,true)添加核心工作
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(核心线程数配置了0,第一个任务进来时,工作线程个数是0),添加一个非核心线程,任务从队列中获取
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
// 如果线程池处于非运行状态,或者BlockingQueue满了,添加非核心线程,添加非核心线程也失败时,执行拒绝策略
else if (!addWorker(command, false))
reject(command);
}
线程池的工作是由工作线程来处理的,工作线程的创建入口是addWorker,看addworker源码前先看一下线程池状态,有如下几种,用32位长度的高三位来表示线程池的状态,其中,running状态是 -1<<29。
// 各个状态值
private static final int RUNNING = -1 << 29; -536870912 (- 01 + 00000000000000000000000000000)
private static final int SHUTDOWN = 0 << 29; 0 ( 00 + 00000000000000000000000000000)
private static final int STOP = 1 << 29; 536870912 ( 01 + 00000000000000000000000000000)
private static final int TIDYING = 2 << 29; 1073741824 ( 10 + 00000000000000000000000000000)
private static final int TERMINATED = 3 << 29; 1610612736 ( 11 + 00000000000000000000000000000)
添加工作线程:ThreadPoolExecutor.addWorker(Runnable firstTask, boolean core)
// firstTask 工作线程创建后,要执行的首个任务(可能为空)
// core 创建的线程是否是核心线程
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
// 自旋检查线程池状态
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// 1,如果是运行状态,直接跳过(了解状态值后再看次判断逻辑)
// 2,如果是shutdown状态,firstTask == null (添加的非核心线程),队列内容不为空时,可以尝试后面的添加流程,否则返回失败
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
// 第二次自旋,增加工作线程数量
for (;;) {
int wc = workerCountOf(c);
// 参数core的唯一用处:工作线程数和控制线程数的比较,如果创建的是核心线程,和配置的核心线程数作比较,如果是非核心线程,和配置的最大线程数作比较
// 如果工作线程打到了,允许的最大线程数,返回false,添加失败
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
// cas操作增加工作线程数,如果成功,跳出自旋,如果添加不成功,进入下一次自旋(最终要么在此处添加成功,要么在下一次自旋时添加失败)
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get();
// 再次检查线程池的状态
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 {
// 创建工作线程
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 通过线程池锁,再次检查线程池状态和线程状态
int rs = runStateOf(ctl.get());
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();
}
// 如果工作线程成功添加到了线程池内,启动工作线程的内部线程(newThread(this==worker)),实际调用worker的run方法。
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
// 如果工作线程启动失败,恢复线程池ctl信息,中断工作线程,移除工作线程等操作
addWorkerFailed(w);
}
return workerStarted;
}
创建工作线程的 Worker(Runnable firstTask) 构造方法中,需要注意,关于state 状态的使用
Worker(Runnable firstTask) {
setState(-1); // 设置 aqs中节点状态为 -1 ,不允许内部线程被中断(因为工作线程刚初始化还未启动),此状态值的恢复在runWorker方法的unlock方法。
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
// 线程池shutdownNow时会调用中断工作线程的方法,此处会中断worker内实际工作的线程。
private void interruptWorkers() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers)
w.interruptIfStarted();
} finally {
mainLock.unlock();
}
}
void interruptIfStarted() {
Thread t;
// 如果当前worker刚初始化,state 为-1时,无需中断内部线程
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
worker.t.start(),实际执行的是worker的run方法,因为worker本身实现了Runable接口。代码部分:this.thread = getThreadFactory().newThread(this)
worker 的run方法,又是调用的线程池的runWorker方法,下面是 runWorker(Worker w)的代码:
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // 此处通过worker的unlock方法恢复AQS中node的state值,目的是可以通过interrupt方式中断工作线程
boolean completedAbruptly = true;
try {
// 如果firstTask != null 直接执行当前任务的run方法
// 如果task 为空时,需要从阻塞队列中通过getTask获取一个待执行的任务,如果也返回null,结束while 循环,最终执行工作线程退出逻辑
// runWork 执行完毕后,worker的run方法执行完毕,worker.t线程执行完毕,线程可以正常回收。
while (task != null || (task = getTask()) != null) {
w.lock();
// 通过worker加锁后,再次根据线程池状态判断是否进行当前线程的中断
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
// 可扩展的线程池预留接口,任务执行前可以自定义一些处理,比如任务执行状态的更新等
beforeExecute(wt, task);
Throwable thrown = null;
try {
// 真正要执行的run方法,递交到线程池里的Runable接口或者Callable接口
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 = null;
w.completedTasks++;
w.unlock();
}
}
// 突然中止标记,为了减少线程池中工作线程的数量,正常执行到此处时,gettask方法内已经做了线程数量的累减,此处标记为false,finally方法内的工作线程退出时,就不需要再次累减工作线程的数量了
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
通过getTask()方法,从队列中获取待执行的任务
private Runnable getTask() {
boolean timedOut = false; // poll 方法获取任务是否超时
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// 检查线程池状态和队列情况,如果线程池停止或者队列为空,需要结束工作线程,返回null之前,对线程池内工作线程的数量做累减(返回null后,工作线程的run方法就会执行完毕,执行完毕后正常执行线程的退出流程)
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// 获取任务超时或者工作线程数大于核心线程数时,当前工作线程可以被淘汰(使用带超时时间的poll方法获取任务)
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
// 工作线程数量,大于最大线程数量;或者需要被淘汰时,cas方式减少工作线程数量,并返回null,结束工作线程的run方法
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
// 核心工作线程,用阻塞式的take方法获取待执行任务,非核心工作线程,使用带超时时间的阻塞式poll方法获取待执行任务
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
// 如果顺利获取到了待执行任务,返回给worker,在worker的while循环中,执行r.run(),达到worker中线程复用的效果。
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
最终回到线程池本身的最上层,工作线程已满且队列已满,或者线程池已经停止或者正在停止时,新来的任务会执行拒绝策略:
(RejectedExecutionHandler)handler.rejectedExecution(command(待执行), this(当前线程池))
拒绝策略比较简单就不再贴源码,线程池提供的拒绝策略有以下4种,默认的是AbortPolicy,源码:defaultHandler = new AbortPolicy();
AbortPolicy:直接抛异常:throw new RejectedExecutionException("Task " + r.toString() + " rejected from " + e.toString());
DiscardPolicy:空的实现方法,递交上来的任务进入拒绝策略后,什么也不做,直接丢弃。
DiscardOldestPolicy:从阻塞队列中通过poll()方法,移除一个等待最久的任务直接丢弃,再次尝试将当前任务递交线程池 e.execute®。
CallerRunsPolicy:直接在提交任务的主线程里直接执行 r.run()方法。
除这四种之外,还可以自己实现RejectedExecutionHandler接口,自己定制一个拒绝策略,比如等待几秒,重新添加到线程池,可以根据当前任务id设置重试次数;也可以根据当前的任务信息,恢复任务状态,或者其他处理等。