前言
线程池的作用:
- java中的线程是基于内核线程实现的,这样就会带来两个问题
- 线程的创建需要进行系统调用,这样就会在用户态和内核态进行切换,导致较多的上下文切换
- 因为java线程和内核线程是1:1的,那么每个线程都得消耗一定的内核空间,因为需要维护线程栈。
- 所以线程池的作用就可以提高资源利用率,可以重复使用已经创建号的线程,减少重复创建,销毁造成的开销。
一、使用案例
这里我们创建了一个线程池,并且批量往线程池中提交任务。
public class ThreadPoolExecutorTest {
private static final int taskCount = 50;//任务数
public static void main(String[] args) throws InterruptedException {
AtomicInteger integer = new AtomicInteger();
ThreadPoolExecutor executor = new ThreadPoolExecutor(
10,//核心线程数
20,//最大线程数
5,//非核心回收超时时间
TimeUnit.SECONDS,//超时时间单位
new ArrayBlockingQueue<>(30)//任务队列);
System.out.println("总任务数:" + taskCount);
long start = System.currentTimeMillis();
//模拟任务提交
for (int i = 0; i < taskCount; i++) {
Thread thread = new Thread(() -> {
try {
Thread.sleep(500);//模拟执行耗时
System.out.println("已执行" + integer.addAndGet(1) + "个任务");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
try {
//注意这里我try起来了,默认拒绝策略会报错
executor.execute(thread);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
long end = 0;
while (executor.getCompletedTaskCount() < 50) {
end = System.currentTimeMillis();
}
System.out.println("任务总耗时:" + (end - start));
}
}
二、重要组件
从上面的使用案例中我们发现线程池中有几个比较重要的组件。
2.1、corePoolSize(核心线程数)
线程池内部需要维持的一个最小的工作线程数量。工作线程数量不足这个数量的时候,新来的任务都会交给一个新建的工作线程,任务不会被放入队列。工作线程数达到这个数量的时候,新来的任务都会直接放入队列,所有的工作线程会主动去轮询领任务,这里每新来一个任务就创建一个核心线程主要是为了保证核心线程尽快达到我们设置的数量,这样如果之后有很多任务涌进来,这些已创建好的核心线程就可以马上做好准备处理这些任务了。
2.2、maximumPoolSize(最大线程数)
线程池内部会限制一个最大的工作线程数量。当阻塞队列满了,并且当前线程数量已经大于等于核心线程数了,那么只有小于最大线程数的时候才可以继续创建线程处理。
2.3、workQueue(任务队列)
线程池内部维护了一个队列用来存储待处理的任务,每个工作线程都可以从队列获取任务进行处理,这里任务放入队列的时机是:核心线程数都正在执行,没时间处理,那么会先把任务放入队列中。
这里你可能想问为啥不再继续创建线程,反正还没到最大线程数,再创建线程当然可以,但是如果核心线程数处理的很快,可能过了很短的时间就能空闲出来继续处理任务,那么就可以避免掉线程频繁创建销毁的开销了。
2.4、RejectedExecutionHandler(拒绝策略)
线程池如果workerQueue是有界队列并且已经满了,并且线程数也到了最大线程数的上限了,此时仍然有任务一直提交过来,但是现在已经没办法处理了,已经超负载了,这个时候就需要有一种处理机制来帮我拒绝掉这些任务。
线程池默认提供了AbortPolicy,DiscardPolicy,DiscardOldestPolicy,CallerRunsPolicy,以及自定义策略这五种拒绝策略,默认采用的是 AbortPolicy
2.5、keepAliveTime(线程存活时间)
这个是线程保持空闲的时间,可能很长一段时间都没有任务提交过来,那么线程池中的线程都处于空闲状态,但是他们还占用资源,所以当线程数量超过了核心线程数,并且有线程超过了keepAliveTime时间一直处于空闲状态,那么就会把他干掉,直到线程数减少到核心线程数。
注:默认是针对非核心线程数,如果我们把参数allowCoreThreadTimeOut设置为true,那么针对核心线程数也生效
三、线程池处理流程
四、线程池状态机
线程池状态 | 状态描述 |
---|---|
RUNNING | 正常的运行状态,此时可以正常接收和处理任务 |
SHUTDOWN | 关闭状态,只能通过调用shutdown()方法达到此状态。此时可以处理组阻塞队列中的任务,但不再接受新任务,并且中断空闲的工作线程 |
STOP | 停止状态,只能通过调用shutdownNow()方法达到此状态。此时清除队列中的未处理的任务,中断所有的(空闲的+正在执行任务的)工作线程。(不建议直接调用shutdownNow,会导致业务逻辑异常终止,会带来很多不可预知的问题) |
TIDYING | 所有的任务都执行完毕后,(同时也涵盖了阻塞队列中的任务),当前线程池中的活动的线程数量降为0,从SHUTDOWN和STOP自动流转到此状态,然后将会调用terminated方法。 |
TERMINATED | 线程池的终止状态,从TIDYING状态自动流转到此状态,当terminated方法执行完毕后,线程池会进入该状态 |
五、线程池状态及线程数管理
- 这里把一个int类型的变量复合使用,高3位表示线程池运行状态,低29位表示线程池线程数量
- 线程池对应状态(变化的只有高3位):
- 运行状态: 101 00000 00000000 00000000 00000000
- 关闭状态 : 000 00000 00000000 00000000 00000000
- 强制关闭 : 001 00000 00000000 00000000 00000000
- 结束状态 : 010 00000 00000000 00000000 00000000
- 死亡状态 : 011 00000 00000000 00000000 00000000
- 提供了3个函数,用于获取ctl的相关状态
- runStateOf:获取当前线程池的运行状态
- c:运行时的ctl状态值
- ~CAPACITY:0,原本是29个1,线程取反变成29个0了。
- c & ~CAPACITY:c是32位bit的值,然后与上29个0,那么低29位就全变成0了,那么最后取到的就是ctl的高3位的值了。
- c : 运行期间的ctl状态值
- CAPACITY : 29个1
- c & CAPACITY : c和29个1做与操作,那么高3位就变成0了,所以最终只保留低29bit位,由此可以得到当前线程池中已创建的工作线程数量
- ctlOf(int rs, int wc) :
- rs : 当前需要改变的最新运行状态,例如由RUNNING变为SHUTDOWN的情况下,那么rs传送的就是SHUTDOWN值
- wc : 当前线程池中已创建的工作线程数量,根据workerCountOf()获取
- rs | wc : 最终是高3bit位更换了状态,低29bit位保留了当前创建的工作线程数
//运行时的状态,默认初始化后就是RUNNING状态
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
//29bit位,Integer.size=32
private static final int COUNT_BITS = Integer.SIZE - 3;
//线程池中工作线程的最大容量: 1左移29位减1 ⬇️
// 11111 11111111 11111111 11111111 (29个1)
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
//运行状态: 101 00000 00000000 00000000 00000000
private static final int RUNNING = -1 << COUNT_BITS;
// 关闭状态 : 000 00000 00000000 00000000 00000000
private static final int SHUTDOWN = 0 << COUNT_BITS;
// 强制关闭 : 001 00000 00000000 00000000 00000000
private static final int STOP = 1 << COUNT_BITS;
// 结束状态 : 010 00000 00000000 00000000 00000000
private static final int TIDYING = 2 << COUNT_BITS;
// 死亡状态 : 011 00000 00000000 00000000 00000000
private static final int TERMINATED = 3 << COUNT_BITS;
// 获取当前线程池的运行状态
private static int runStateOf(int c) {
//c是运行期间的ctl状态值, ~CAPACITY : 0 (即29个1全部变为0)
//这里两个状态进行&操作就做到了只保留高3bit,可以直接得到当前线程池的运行状态
return c & ~CAPACITY;
}
// 获取当前线程池中已创建的工作线程数量
private static int workerCountOf(int c) {
//c是运行期间的ctl状态值, CAPACITY:29个1
//两个状态&就做到了只保留低29bit位,因此可以得到当前线程池中已创建的工作线程数量
return c & CAPACITY;
}
// 更换运行状态,并且根据当前线程池中已创建的工作线程数量(workerCountOf())来保留当前的工作线程数量;
// 这个函数最终的作用就是改变了高3bit为的运行状态,并且低29bit位保留了之前创建的工作线程数量
private static int ctlOf(int rs, int wc) {
//rs:当前需要改变的最新运行状态,例如由RUNNING变为SHUTDOWN的情况下,那么rs传送的就是SHUTDOWN值
//wc:当前线程池中已创建的工作线程数量,根据workerCountOf()获取
//两个状态|就实现了高3bit位更换了状态,低29bit位保留了当前创建的工作线程数
return rs | wc;
}
六、ThreadPoolExecutor执行流程源码剖析
6.1、execute()方法
- 对任务做判空,为空则抛npe
- 从ctl中获取工作线程数量,判断如果小于核心线程数,就创建核心线程
- 如果到达了核心线程数,则加入任务队列
- 如果任务队列满了,则创建非核心线程
- 如果到达了最大数量,则执行拒绝策略
public void execute(Runnable command) {
// 任务不能为空
if (command == null)
throw new NullPointerException();
// 控制变量(高3位存储状态,低29位存储工作线程的数量)
int c = ctl.get();
// 1。如果工作线程数量小于核心数量
if (workerCountOf(c) < corePoolSize) {
// 就添加一个工作线程(核心)
if (addWorker(command, true))
return;
// 重新获取下控制变量
c = ctl.get();
}
// 2。如果达到了核心数量且线程池是运行状态,任务入队列
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
// 再次检查线程池状态,如果不是运行状态,就移除任务并执行拒绝策略。
if (!isRunning(recheck) && remove(command))
reject(command);
// 容错检查工作线程数量是否为0,如果为0就创建一个 //corePoolSize=0
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
// 3。任务入队列失败,尝试创建非核心工作线程
else if (!addWorker(command, false))
// 非核心工作线程创建失败,执行拒绝策略。
reject(command);
}
6.1.1、addWorker()方法
该方法就是创建工作线程,流程如下:
- 检查当前线程池的运行状态:
- 如果当前处于 SHUTDOWN 并且 firstTask == null 都为true的情况下,说明 SHUTDOWN状态下不接收任务了;并且 任务队列不为空 为true的情况下,说明任务队列还存在任务需要处理;虽然目前处于SHUTDOWN,但还是会继续处理阻塞队列的任务,只是不接收任务了
- 如果处于STOP 状态或者及其之后则不创建线程,不进行处理了
- 再次获取当前的线程数,判断如果当前已经达到了线程最大容量 或者已经达到要创建的线程数的上限则不进行创建。
- 如果没有到上限,则通过CAS修改ctl的状态值,给他++,如果修改成功了则跳出循环执行后续创建线程操作,如果CAS失败,则再次获取当前线程数,如果线程数遍了,则自旋重新走一遍流程
- 如果修改ctl的状态值成功,那么创建Wroker对象并分配当前任务,如果worker创建成功了,则判断当前线程池的状态处于Running状态,则把创建的线程添加到workers中,然后记录下largestPoolSize也就是线程池中出现过的最大功线程数量。
- 如果添加到workers成功了,则调用其start方法启动worker线程
- 如果没启动成功,则做一个删除收尾的操作
private boolean addWorker(Runnable firstTask, boolean core) {
// 判断有没有资格创建新的工作线程
// 主要是一些状态/数量的检查等等
// 这段代码比较复杂,可以先跳过
retry: for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
// 线程池状态检查
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;
// 数量加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
}
}
// 如果上面的条件满足,则会把工作线程数量加1,然后执行下面创建线程的动作
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()) // precheck that t is startable
throw new IllegalThreadStateException();
// 添加到工作线程队列
workers.add(w);
// 还在池子中的线程数量(只能在mainLock中使用)
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
// 标记线程添加成功
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
// 线程添加成功之后启动线程
t.start();
workerStarted = true;
}
}
} finally {
// 线程启动失败,执行失败方法(线程数量减1,执行tryTerminate()方法等)
if (!workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
6.1.1、Worker内部类
- 这里worker类可以看作是对工作线程的包装,内部维护了一个Thread,而且worker继承了AQS,使用了独占式获取锁的方式,并且是不可重入的:
- 防止同一时刻同一工作线程执行多个任务;
- 防止一些工作线程在执行当前任务的过程中被中断,保证正在执行的任务可以顺利执行完;
- 不可重入:可以获取到当前活跃状态(空闲或者执行)的工作线程;
- 启动worker类会调用到start方法,然后会调用到worker#run方法中
// Worker继承自AQS,自带锁的属性
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
// 真正工作的线程
final Thread thread;
// 第一个任务,从构造方法传进来
Runnable firstTask;
// 完成任务数
volatile long completedTasks;
// 构造方法
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
// 使用线程工厂生成一个线程
// 注意,这里把Worker本身作为Runnable传给线程
this.thread = getThreadFactory().newThread(this);
}
// 实现Runnable的run()方法
public void run() {
// 调用ThreadPoolExecutor的runWorker()方法
runWorker(this);
}
// 省略锁的部分
}
6.1.3、ThreadPoolExecutor#run方法
这里要能够看出来工作线程Thread启动的时候实际是调用的Worker的run()方法,进而调用的是ThreadPoolExecutor的runWorker()方法。
public void run() {
/// 调用ThreadPoolExecutor的runWorker()方法
runWorker(this);
}
6.1.4、Worker#run方法
- 这里会在一个循环中不断调用getTask获取任务执行,直到获取任务为空则退出循环,线程也被回收。
- 任务执行的时候会进行加锁,防止同一工作线程执行多个任务
- 检查线程池状态,判断是否需要中断线程,如果当前线程池状态是大于等于STOP状态的,那么肯定要中断线程,会根据wt.isInterrupted()来判断是否需要中断,如果已经中断了则不需要,如果线程池状态不是大于等于STOP状态的,则继续判断当前线程是不是中断状态,如果是并且当前状态已经处理STOP及其之后的状态了,则在根据wt.isInterrupted()判断是否需要中断线程。
- Java的中断线程并不是直接中断线程,而是一种协调机制,上面调用了 wt.interrupt(),中断标识变成true,但是还是会继续往下执行,task对象可以根据isInterrupted()来判断当前线程是中断状态
- 调用beforeExecute,这个是个钩子方法,可以子类实现在任务执行前做一些处理。
- 最终会调用task#run方法执行任务
- 执行完成后会调用beforeExecute,这也是个钩子方法
- 如果while循环退出了,说明获取到的任务是null,那么会执行processWorkerExit回收线程资源
final void runWorker(Worker w) {
// 工作线程
Thread wt = Thread.currentThread();
// 任务
Runnable task = w.firstTask;
w.firstTask = null;
// 强制释放锁(shutdown()里面有加锁)
// 这里相当于无视那边的中断标记
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
// 取任务,如果有第一个任务,这里先执行第一个任务
// 只要能取到任务,这就是个死循环
// 正常来说getTask()返回的任务是不可能为空的,因为前面execute()方法是有空判断的
// 那么,getTask()什么时候才会返回空任务呢?
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;
// 完成任务数加1
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
// 到这里肯定是上面的while循环退出了。
processWorkerExit(w, completedAbruptly);
}
}
6.1.4、ThreadPoolExecutor#getTask方法
- 自旋获取任务,判断当前线程池运行状态,如果当前运行状态处于SHUTDOWN 之后(包括SHUTDOWN) ,可能需要回收线程, 继续判断如果处于STOP及其之后则不管队列中的任务了,直接回收线程或者处于 SHUTDOWN 并且阻塞队列为空的话也回收线程
- 如果线程池状态正常的话,则获取当前工作线程数量,判断是否允许超时,有两种情况
- 是允许核心线程数超时,这种就是说所有的线程都可能超时
- 是工作线程数大于了核心数量,这种肯定是允许超时的
注:注意,非核心线程是一定允许超时的,这里的超时其实是指取任务超时
- wc > maximumPoolSize的情况是因为可能在此方法执行阶段同时执行了setMaximumPoolSize方法重置了,timed && timedOut 如果为true,表示当前存在需要回收的非核心线程,并且上次从阻塞队列中获取任务发生了超时,需要回收非核心线程,接下来判断,如果当前工作线程数量至少2条时,或者阻塞队列是空的,那么尝试将workerCount减1,即回收非核心线程,如果wc == 1时,并且阻塞队列还有任务,那么需要保留至少一条线程
- 下面是从任务队列中取任务,根据timed判断是否需要从阻塞队列中超时阻塞式获取任务,如果获取到任务直接返回,如果在规定时间内没有获取到任务,则把timedOut设置为true,那么下次自旋会进行回收擦欧总。
private Runnable getTask() {
// 是否超时
boolean timedOut = false; // Did the last poll() time out?
// 死循环
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
// 线程池状态是SHUTDOWN的时候会把队列中的任务执行完直到队列为空
// 线程池状态是STOP时立即退出
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
// 工作线程数量
int wc = workerCountOf(c);
// Are workers subject to culling?
// 是否允许超时,有两种情况:
// 1. 是允许核心线程数超时,这种就是说所有的线程都可能超时
// 2. 是工作线程数大于了核心数量,这种肯定是允许超时的
// 注意,非核心线程是一定允许超时的,这里的超时其实是指取任务超时
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
// 超时判断(还包含了一些容错判断)
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
// 超时了,减少工作线程数量
if (compareAndDecrementWorkerCount(c))
return null;
// 减少工作线程数量失败。
continue;
}
try {
// 真正取任务的地方
// 默认情况下,只有当工作线程数量大于核心线程数量时,才会调用poll()方法触发超时调用
Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take();
// 取到任务了就正常返回
if (r != null)
return r;
// 没取到任务表明超时了,回到continue那个if中返回null
timedOut = true;
} catch (InterruptedException retry) {
// 捕获到了中断异常
// 中断标记是在调用shutDown()或者shutDownNow()的时候设置进去的
// 此时,会回到for循环的第一个if处判断状态是否要返回null
timedOut = false;
}
}
}
6.1.5、ThreadPoolExecutor#processWorkerExit方法
- 判断是否是异常退出,异常退出则对线程数做扣减
- 加锁,然后统计任务完成次数,并删除工作线程
- 判断当前线程池是否设置为TIDYING、TERMINATED状态
- 获取当前运行状态,判断如果当前运行状态是RUNNING或者是SHUTDOWN,判断如果工作线程是正常退出,则再判断工作队列是否还有任务,如果有任务则至少保留一个工作线程,如果当前1个工作线程都没有,则会再次创建一个工作线程。
private void processWorkerExit(Worker w, boolean completedAbruptly) {
//是否为异常而退出
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
//是,则需要设置ctl线程数减1,因为异常情况下线程数没扣件
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//统计任务完成次数
completedTaskCount += w.completedTasks;
//删除工作线程
workers.remove(w);
} finally {
mainLock.unlock();
}
//判断当前线程池是否设置为TIDYING、TERMINATED状态
tryTerminate();
//获取当前运行状态
int c = ctl.get();
//如果当前运行状态是 RUNNING 或者 SHUTDOWN
if (runStateLessThan(c, STOP)) {
//工作线程是正常退出
if (!completedAbruptly) {
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
//workeQueue存在任务,至少保留一个工作线程
if (min == 0 && !workQueue.isEmpty())
min = 1;
//当前工作线程数大于1条,不创建了
if (workerCountOf(c) >= min)
return; // replacement not needed
}
// 创建线程 :
//2种情况:异常退出,重新创建线程 或者
//当前运行状态是 RUNNING 或者 SHUTDOWN,线程池中还存在任务,需要至少保留1条线程
addWorker(null, false);
}
}
七、ThreadPoolExecutor生命周期源码剖析
7.1、RUNNING
创建线程池的时候就会初始化ctl,而ctl初始化为RUNNING状态,所以线程池的初始状态就为RUNNING状态。
// 初始状态为RUNNING
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
7.2、SHUTDOWN
执行shutdown()方法时把状态修改为SHUTDOWN,这里肯定会成功,因为advanceRunState()方法中是个自旋,不成功不会退出。
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
// 修改状态为SHUTDOWN
advanceRunState(SHUTDOWN);
// 标记空闲线程为中断状态
interruptIdleWorkers();
onShutdown();
} finally {
mainLock.unlock();
}
tryTerminate();
}
private void advanceRunState(int targetState) {
for (;;) {
int c = ctl.get();
// 如果状态大于SHUTDOWN,或者修改为SHUTDOWN成功了,才会break跳出自旋
if (runStateAtLeast(c, targetState) ||
ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))
break;
}
}
7.3、STOP
执行shutdownNow()方法时,会把线程池状态修改为STOP状态,同时标记所有线程为中断状态,至于线程是否响应中断其实是在队列的take()或poll()方法中响应的,最后会到AQS中,它们检测到线程中断了会抛出一个InterruptedException异常,然后getTask()中捕获这个异常,并且在下一次的自旋时退出当前线程并减少工作线程的数量。最后shutdownNow会返回阻塞队列中没有被处理的任务。
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
// 修改为STOP状态
advanceRunState(STOP);
// 标记所有线程为中断状态
interruptWorkers();
//返回阻塞队列中没有被处理的任务
tasks = drainQueue();
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks;
}
7.4、TIDYING
当执行shutdown()或shutdownNow()之后,如果所有任务已中止,且工作线程数量为0,就会进入这个状态,其实我们在上面可以看到两个方法最后都会调用tryTerminate方法。
final void tryTerminate() {
for (;;) {
// 获取当前运行状态
int c = ctl.get();
// 1. 当前运行状态是 RUNNING 暂时不设置TIDYING、TERMINATED
// 2. 或者 当前运行状态 处于 TIDYING 或者 TERMINATED, 那么也是暂时不设置TIDYING、TERMINATED
// 3. 或者 就是在 SHUTDOWN ,并且存在任务需要处理,那么也是暂时不设置TIDYING、TERMINATED
// 4. 否则 就是STOP,那么需要往下走
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
return;
// 执行到这里 说明处于 STOP状态, 但是还存在工作线程没处理完,帮忙中断线程
// 如果线程数量不为0,则中断一个空闲的工作线程,并返回
if (workerCountOf(c) != 0) {
interruptIdleWorkers(ONLY_ONE);
return;
}
// 执行到这里 说明处于 SHUTDOWN(阻塞队列没有任务了) 或者 STOP
// 那么设置 TIDYING、TERMINATED
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 设置 TIDYING
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
try {
// terminated方法默认什么都不做,留给子类实现
terminated();
} finally {
// 设置 TERMINATED
ctl.set(ctlOf(TERMINATED, 0));
termination.signalAll();
}
return;
}
} finally {
mainLock.unlock();
}
}
}