分析线程池的底层原理,有问题烦请批评指正。
execute()方法
提交任务command到线程池执行。
public void execute(Runnable command) {
// 0. 如果任务为空,抛出NPE
if (command == null)
throw new NullPointerException();
// 获取ctl属性=线程状态+线程数
int c = ctl.get();
// 1. 如果当前池中线程数量小于核心线程数
if (workerCountOf(c) < corePoolSize) {
// 向workers新增核心线程执行command任务
if (addWorker(command, true))
return;
// 添加线程失败
c = ctl.get();
}
// 2. 如果池是RUNNING状态,并且可以添加任务到阻塞队列
if (isRunning(c) && workQueue.offer(command)) {
// 二次校验:可能在添加任务到队列后,
// 不再需要添加一个线程(过程中有线程死亡)或者线程池关闭,池状态发生改变。
int recheck = ctl.get();
// 2.1 如果线程池已关闭,则删除队列任务【巧妙的写法 !false && remove()】
if (!isRunning(recheck) && remove(command))
// 执行拒绝策略
reject(command);
// 2.2 否则如果池中线程数为0,则新增一个线程
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
// 3. 如果队列已满,则新增线程;如果新增失败,就执行拒绝策略。
else if (!addWorker(command, false))
reject(command);
}
上面代码执行的步骤使用文字来叙述一下。
1 如果当前工作线程数量,小于核心线程的数量,就尝试添加一个新线程,并把传入的command作为新添加的线程的第一个任务,添加成功就返回。调用addWorker方法会以原子方式检查runState和workerCount并且通过返回false来避免在假唤醒的时候添加线程。
2 如果当前线程池正在运行并且任务可以成功加入到队列中,我们仍然需要再次检查线程池(检查线程池的运行状态,当前线程池中的工作线程数量)。为什么要再次检查呢?
当任务被添加到了阻塞队列前,线程池处于RUNNING 状态,但如果在添加到队列成功后,线程池进入了SHUTDOWN 状态或者其他状态,这时候是不应该再接收新的任务的,所以需要把这个任务从队列中移除,并且拒绝该任务。同样,在没有添加到队列前,可能有一个有效线程,但添加完任务后,这个线程可能因为闲置超时或者异常被干掉了,这时候需要创建一个新的线程来执行任务。
2.1 如果线程池状态线程池是SHUTDOWN了,并且能成功的把任务从队列中移除,就拒绝任务。
2.2 如果任务加入到队列了,但是当前没有工作线程就添加一个线程。
3 如果我们不能把任务加入队列,那么我们尝试添加一个新线程,如果添加失败(线程池已经shut down 或者已经饱和了),就拒绝任务。
所谓拒绝,就是调具体拒绝策略的rejectedExecution()方法。
final void reject(Runnable command) {
handler.rejectedExecution(command, this);
}
addWorker方法
其中addWorker是execute()方法的核心方法,用于原子校验线程池状态和线程数,并新增线程。
private boolean addWorker(Runnable firstTask, boolean core) {
// 跳转标签
retry:
// 外层循环
for (;;) {
// 获取ctl属性与线程池状态
int c = ctl.get();
int rs = runStateOf(c);
// 1. 线程池状态校验,仅在必要时检查队列是否为空。
// 返回false总结
// 1.1 线程池是STOP、TIDYING、TERMINATED
// 2.2 线程池是SHUTDOWN,并且任务不为null或阻塞队列为空
if (rs >= SHUTDOWN && // 线程池非RUNNING状态
! (rs == SHUTDOWN && // 特殊情况:池状态为SHUTDOWN,任务为null,工作队列不为空
firstTask == null &&
! workQueue.isEmpty()))
return false;
// 内层循环,CAS增加线程个数
for (;;) {
int wc = workerCountOf(c);
// 2. 当前线程个数超过上限,返回false
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
// 3. CAS成功增加线程数,直接退出外层循环
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // 再次读取ctl值
// 如果CAS增加线程失败,校验线程池状态是否发生改变
// 4. 如果池状态改变,跳到外层循环,重新获取池状态;
if (runStateOf(c) != rs)
continue retry;
// 未改变,则内层循环再次进行CAS操作
}
}
// 此时完成CAS操作,线程数增加
// 5. 添加worker的逻辑
boolean workerStarted = false; // 标记工作线程是否启动
boolean workerAdded = false; // 标记worker是否添加到workers
Worker w = null; // 工作线程
try {
// 创建worker
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
// 此时,线程工厂成功创建了一个线程
final ReentrantLock mainLock = this.mainLock; // 全局独占锁-mainLock
// 加锁:实现worker添加操作同步
mainLock.lock();
try {
// 再次检查线程池状态。ThreadFactory失败或者获取锁前shutdown,退出
int rs = runStateOf(ctl.get());
// 线程池状态为RUNNING,或者池状态为SHUTDOWN,并且任务为null时
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
// 如果线程已经启动,抛出异常
if (t.isAlive())
throw new IllegalThreadStateException();
// 添加worker到线程池workers线程集合
workers.add(w);
int s = workers.size();
// 改变最大线程数,池大小动态变化
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true; // 更新任务添加标识
}
} finally {
// 解除mainLock锁
mainLock.unlock();
}
// 任务添加后,启动线程
if (workerAdded) {
t.start();
workerStarted = true; // 更新线程启动标识
}
}
} finally {
// 如果线程启动失败,回滚worker创建过程
if (! workerStarted)
addWorkerFailed(w);
}
// 返回任务是否成功启动
return workerStarted;
}
检查根据当前池状态和给定的边界(核心或者最大)是否可以添加新worker。
代码主要分为两部分,一部分是通过CAS增加线程数,另一部分是加锁添加任务到workers,并启动线程。
如果线程池关闭、线程工厂创建失败(线程工厂返回空,或异常(线程创建时OOM)),会返回false,并回滚上面的操作。
回滚过程,包括
- 从workers删除任务;
- CAS减少线程数
- 重新校验终止,避免该worker的存在阻碍了终止。
private void addWorkerFailed(Worker w) {
// 获取全局锁
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (w != null)
// 删除worker任务
workers.remove(w);
// CAS较少线程数
decrementWorkerCount();
// 尝试终止
tryTerminate();
} finally {
mainLock.unlock(); // 解锁
}
}
Woker嵌套类–Worker线程类
Worker类实现Runnable接口,用来具体承载任务;继承AQS,自己实现不可重入的独占锁。锁状态state有3个:-1表示创建时状态,0表示锁未获取状态,1表示锁已获取状态。
private final class Worker extends AbstractQueuedSynchronizer implements Runnable
{
// 执行当前任务的线程,如果为null说明线程工厂添加失败
final Thread thread; // final修饰
// 要执行的任务,可能为null
Runnable firstTask;
// 每个线程的任务计数器,标记当前线程执行了多少个任务
volatile long completedTasks;
Worker(Runnable firstTask) {
setState(-1); // runWorker执行前禁止中断
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this); // 创建一个线程
}
// Worker实现run方法
public void run() {
runWorker(this);
}
}
worker在run方法中调用了ThreadPoolExecutor的runWorker方法,并传入worker实例自身。
runWorker
runWorker执行前禁止中断,是因为shutdownNow()方法中在中断Worker时会对state做校验,state>=0才会去中断线程。
inal void runWorker(Worker w) {
// 当前线程即worker中的线程
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // 设置state为0,运行中断
boolean completedAbruptly = true; // 快速结束标识
try { // 第1层trycatch
// 校验firstTask和阻塞队列中任务是否为空
while (task != null || (task = getTask()) != null) {
w.lock(); // 加锁
// 如果调用了shutdownNow()方法,池是STOP,确保线程被中断
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt(); // 中断线程
try { // 第2层trycatch
// 执行任务前,执行钩子方法
beforeExecute(wt, task);
Throwable thrown = null;
try { // 第3层trycatch
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; // 清理worker中的任务,下次循环获取新任务
w.completedTasks++;
w.unlock();
}
} // while循环结束
completedAbruptly = false;
} finally {
// 清理worker
processWorkerExit(w, completedAbruptly);
}
}
- while 循环,如果Worker的firstTask不为null,或者getTask() 从队列中获取任务不为null。注意从队列中获取任务可能会阻塞。如果Worker能够取到任务,就不停的执行,这就是为什么线程可以复用不退出的原因。
任务分为两种,一种是创建Woker的那个firstTask,另一种是阻塞队列中的任务。
如果两种任务均为null,或者线程池状态改变,completedAbruptly快速结束标识会保持为true,进行worker清理操作。 - 在适当的时候中断Worker的工作线程wt。
- 执行任务。
执行任务时加锁,避免任务运行时,被其他线程调用shutdown中断。除非线程池是STOPPING状态,否则不会中断线程。
beforeExecute()方法在第2层trycatch内,如果抛出异常退出,任务不再执行,只会执行后面的两层finally操作。completedAbruptly值仍为true,清理worker线程。
任何异常,都会导致清理线程。 - 如果没有可执行的任务,或者异常终止则Worker停止工作,Worker的工作线程也就结束了。
processWorkerExit()方法用来清理将要死亡的线程。
private void processWorkerExit(Worker w, boolean completedAbruptly) {
// 1. 如果是突然退出,CAS减少线程数
if (completedAbruptly)
// 只在此处和getTask()方法中调用!!
decrementWorkerCount();
// 2. 统计线程池完成的线程数,并从工作线程集中移除当前Worker
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
completedTaskCount += w.completedTasks;
workers.remove(w);
} finally {
mainLock.unlock();
}
// 3. 试图终止线程池
tryTerminate();
// 获取ctl属性
int c = ctl.get();
// 4. 如果线程池未终止,判断是否新增worker
// 线程池状态是RUNNING或者SHUTDOWN
if (runStateLessThan(c, STOP)) {
if (!completedAbruptly) {
// 如果worker不是突然退出
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
if (min == 0 && ! workQueue.isEmpty())
min = 1;
if (workerCountOf(c) >= min)
return; // replacement not needed
}
// 4.1 如果是突然退出,直接添加新worker
// 4.2 如果正在运行的线程少于corePoolSize,则新增worker
// 4.3 如果此时阻塞队列不为空,但是工作线程数为0,则新增worker
addWorker(null, false);
}
}
getTask()方法用来从阻塞队列中获取任务
// 执行阻塞或者定时等待任务
private Runnable getTask() {
boolean timedOut = false; // 最后的poll操作是否超时
// 死循环
for (;;) {
int c = ctl.get();
int rs = runStateOf(c); // 线程池状态
// 1. 循环退出条件1:线程状态
// 1.1 线程池是STOP、TIDYING、TERMINATED状态
// 1.2 线程池非RUNNING状态,并且任务队列为空,返回null
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
// 减少workerCount数
decrementWorkerCount();
return null;
}
// 当前池中woker数
int wc = workerCountOf(c);
// 是否结束当前woker
// allowCoreThreadTimeOut默认为false(核心线程即使空闲,仍然可以存活,即不使用超时)
// coreThread能够超时,或者当前woker数大于corePoolSize时,当前woker可以超时
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
// 2. 循环退出条件2:线程数
// 2.1 worker的数量大于maximumPoolSize
// 2.2 worker等待从队列中获取任务时超时
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
// 减少worker数,并返回null
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
Runnable r = timed ? // 是否可超时
// 如果可以超时,使用poll方法,在keepAliveTime时间内获取任务【keepAliveTime超时的使用】
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
// 不可以超时,则一直等待直到获取到任务为止【核心线程不退出的原因】
workQueue.take();
if (r != null)
// 成功获取到任务,直接返回
return r;
// 走到这一步,说明从任务队列中获取任务超时了。
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
方法只有两种结果,一种是成功获取到任务,另一种是当前worker必须退出时返回null,此时减少wokerCount数。
当前worker在遇到以下原因时必须停止返回null。
- worker的数量大于maximumPoolSize
- 线程池大于等于STOP状态
- 线程池是SHUTDOWN状态,并且阻塞队列没有任务
- worker等待从队列中获取任务时超时【着重理解】
最最核心的代码
Runnable r = timed ? // 是否可超时
// 如果可以超时,使用poll方法,在keepAliveTime时间内获取任务【keepAliveTime超时的使用】
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
// 不可以超时,则一直等待直到获取到任务为止【核心线程不退出的原因】
workQueue.take();
shutdown方法–温柔的关闭方式
public void shutdown() {
// 获取全局锁--方法中有锁重入
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 1. 权限校验
checkShutdownAccess();
// 2. 校验状态。如果状态大于等于SHUTDOWN,直接返回;否则设置线程池状态为SHUTDOWN
advanceRunState(SHUTDOWN);
// 3. 中断空闲线程--getTask方法中正在等待任务的线程
interruptIdleWorkers();
onShutdown(); // ScheduledThreadPoolExecutor的hook方法
} finally {
mainLock.unlock();
}
// 尝试将线程状态转变为TERMINATED
tryTerminate();
}
执行一次顺序关闭,执行以前提交的任务,并且拒绝新任务。
为什么可以中断空闲线程?
任务队列是Java并发工具中的阻塞队列,其take和poll方法可以自动响应中断。
java.util.concurrent.BlockingQueue.take()/put(E)
tryTerminate()
final void tryTerminate() {
for (;;) {
int c = ctl.get();
// 线程池状态以及woker线程数校验。不符合直接返回
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 设置池状态为TIDYING
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
try {
// 调用线程池的hook方法
terminated();
} finally {
// 设置线程池状态为TERMINATED
ctl.set(ctlOf(TERMINATED, 0));
// 通知因为调用termination变量await方法而阻塞的所有线程。
termination.signalAll();
}
return;
}
} finally {
mainLock.unlock();
}
// else retry on failed CAS
}
}
关于termination变量,是mainLock的线程交互对象。Condition是将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象。
private final Condition termination = mainLock.newCondition();
isShutDown()方法–判断是否执行过shutdown命令
public boolean isShutdown() {
return ! isRunning(ctl.get());
}
private static boolean isRunning(int c) {
return c < SHUTDOWN;
}
判断线程池是否已关闭,状态大于等于SHUTDOWN。
shutdownNow()方法–不温柔的关闭方式
试图停止所有正在执行的线程,暂停处理阻塞队列中的任务,并返回一个等待执行任务的列表。不能保证终止线程,任务可能没有响应中断【不是强制中断】。
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
// 获取锁
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 1. 权限校验
checkShutdownAccess();
// 2. 校验状态。如果状态大于等于STOP,直接返回;否则设置线程池状态为STOP
advanceRunState(STOP);
// 3. 中断所有worker线程
interruptWorkers();
// 4. 将队列任务转移到List中,同时删除队列中任务
tasks = drainQueue();
} finally {
// 解锁
mainLock.unlock();
}
// 试图终止线程池
tryTerminate();
return tasks;
}
内部方法调用
private void interruptWorkers() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 遍历所有wokers集合,中断所有正在执行任务的worker。
for (Worker w : workers)
w.interruptIfStarted();
} finally {
mainLock.unlock();
}
}
// Worker
void interruptIfStarted() {
Thread t;
// worker已经运行,并且线程不为空,没有被中断
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
// 中断线程
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
awaitTermination(long timeout, TimeUnit unit) :
当前线程阻塞,直到某个事件首先发生。三个事件分别是使用shutdown命令后所有任务执行完毕、超时结束或者线程被中断。
public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
// 获取超时时间,纳秒数
long nanos = unit.toNanos(timeout);
// 加锁
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 死循环
for (;;) {
// 如果线程池是TERMINATED,返回true
if (runStateAtLeast(ctl.get(), TERMINATED))
return true;
// 超时时间为0,返回false
if (nanos <= 0)
return false;
// 一直阻塞,直到超时时间结束、中断、被通知(signalled)
nanos = termination.awaitNanos(nanos);
}
} finally {
// 解锁
mainLock.unlock();
}
}
tryTerminate()方法在将线程池状态设为TERMINATED后,会调 termination.signalAll();通知唤醒termination变量上等待的线程。