线程池是什么
线程池就是创建若干个可执行的线程放入一个池(容器)中,有任务需要处理时,会提交到线程池中的任务队列,处理完之后线程并不会被销毁,而是仍然在线程池中等待下一个任务。
使用线程池有什么好处
- 降低资源消耗:通过重复利用现有的线程来执行任务,避免多次创建和销毁线程
- 提高响应速度:因为省去了创建线程这个步骤,所以在拿到任务时,可以立刻开始执行
- 提供附加功能:线程池的可拓展性使得我们可以自己加入新的功能,比如说定时、延时来执行某些线程
- 提高线程的可管理性:线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配、调优和监控
线程池的实现
Java 给我们提供了 Executor 接口来使用线程池。
我们常用的线程池有这两大类:
- ThreadPoolExecutor
- ScheduledThreadPoolExecutor
线程池创建
Executors 也是 java.util.concurrent 包下的成员,它是一个创建线程池的工厂,可以使用静态工厂方法来创建线程池,下面就是 Executors 所能生成一些常用的线程池:
- newSingleThreadExecutor
- 创建一个单线程的线程池,这个线程池只有一个线程在工作,此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
- newFixedTheadPool
- 创建固定大小的线程池。每次提交一个任务就创建一个线程,知道线程达到线程池的最大大小。线程池的大小一旦达到最大值就保持不变,如果某一个线程因为执行异常结束,那么线程池会补充一个新线程。
- newCachedThreadPool
- 创建一个可缓存的线程池,如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(1分钟不执行)的线程,当任务书增加是,此线程池又可以智能的添加新线程来处理任务,此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统能够创建线程的大小。
- newScheduledThreadPool
- 创建一个大小无限的线程池,此线程池支持定时以及周期性的执行任务的需求。
上面这些线程池的底层实现都是由 ThreadPoolExecutor 来提供支持,所以要理解这些线程池的工作原理,就需要先把 ThreadPoolExecutor 搞明白。
ThreadPoolExecutor线程池类参数详解
- corePoolSize:表示核心线程池的大小。当提交一个任务时,如果当前核心线程池的线程个数没有达到 corePoolSize,则会创建新的线程来执行所提交的任务,即使当前核心线程池有空闲的线程。如果当前核心线程池的线程个数已经达到了 corePoolSize,则不再重新创建线程。如果调用了prestartCoreThread()或者 prestartAllCoreThreads(),线程池创建的时候所有的核心线程都会被创建并且启动。
- maximumPoolSize:表示线程池能创建线程的最大个数。如果当阻塞队列已满时,并且当前线程池线程个数没有超过 maximumPoolSize 的话,就会创建新的线程来执行任务
- keepAliveTime:空闲线程存活时间。如果当前线程池的线程个数已经超过了 corePoolSize,并且线程空闲时间超过了 keepAliveTime 的话,就会将这些空闲线程销毁,这样可以尽可能降低系统资源消耗
- unit:时间单位。为 keepAliveTime 指定时间单位
- workQueue:阻塞队列。用于保存任务的阻塞队列,关于阻塞队列可以看这篇文章。可以使用ArrayBlockingQueue, LinkedBlockingQueue, SynchronousQueue, PriorityBlockingQueue
- threadFactory:创建线程的工程类。可以通过指定线程工厂为每个创建出来的线程设置更有意义的名字,如果出现并发问题,也方便查找问题原因
- handler:饱和策略。当线程池的阻塞队列已满和指定的线程都已经开启,说明当前线程池已经处于饱和状态了,那么就需要采用一种策略来处理这种情况。采用的策略有这几种:
- AbortPolicy: 直接拒绝所提交的任务,并抛出RejectedExecutionException异常
- CallerRunsPolicy:只用调用者所在的线程来执行任务
- DiscardPolicy:不处理直接丢弃掉任务
- DiscardOldestPolicy:丢弃掉阻塞队列中存放时间最久的任务,执行当前任务
深入理解线程池
线程池状态
线程池和线程一样拥有自己的状态,在 ThreadPoolExecutor 类中定义了一个 volatile 变量 runState 来表示线程池的状态,线程池有五种状态,分别为RUNNING、SHURDOWN、STOP、TIDYING 、TERMINATED,先附上源码:
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3; // =29
private static final int CAPACITY = (1 << COUNT_BITS) - 1; // =000 11111...
// runState is stored in the high-order bits
private static final int RUNNING = -1 << COUNT_BITS; // 111 00000...
private static final int SHUTDOWN = 0 << COUNT_BITS; // 000 00000...
private static final int STOP = 1 << COUNT_BITS; // 001 00000...
private static final int TIDYING = 2 << COUNT_BITS; // 010 00000...
private static final int TERMINATED = 3 << COUNT_BITS; // 011 00000...
// 线程池的状态
private static int runStateOf(int c) { return c & ~CAPACITY; }
// 线程池中工作线程的数量
private static int workerCountOf(int c) { return c & CAPACITY; }
// 计算ctl的值,等于运行状态“加上”线程数量
private static int ctlOf(int rs, int wc) { return rs | wc; }
- RUNNING:能接受新提交的任务,并且也能处理阻塞队列中的任务
- SHUTDOWN:状态关闭,不在接受新提交的任务,但是能继续处理阻塞队列已保存的让任务
- STOP:不接受新任务,也不处理队列中的任务,会中断正在处理任务的线程
- TIDYING:所有让任务都已终止,workerCount(有效处理让任务线程)状态为0
- TERMINATED:在 terminated() 方法执行结束后进入该状态,此时表示线程池的彻底终止
下面我们来看看这些状态之间是怎么运行的:
这几个状态的转化关系为:
- 调用 shundown() 方法线程池的状态由 RUNNING——>SHUTDOWN
- 调用 shutdowNow() 方法线程池的状态由 RUNNING——>STOP
- 当任务队列和线程池均为空的时候 线程池的状态由 STOP/SHUTDOWN——–>TIDYING
- 当 terminated() 方法被调用完成之后,线程池的状态由 TIDYING———->TERMINATED状态
提交任务
通过 ThreadPoolExecutor 创建线程池处于运行状态,此时线程数量为 0,提交任务后执行过程是怎样的,下面我们看 ThreadPoolExecutor 的执行示意图和执行流程图:
execute 方法执行逻辑有这样几种情况:
- 如果当前运行的线程少于 corePoolSize,则会创建新的线程来执行新的任务
- 如果运行的线程个数等于或者大于 corePoolSize,则会将提交的任务存放到阻塞队列 workQueue 中
- 如果当前 workQueue 队列已满的话,则会创建新的线程来执行任务
- 如果创建新线程会使当前运行的线程超过 maximumPoolSize 的话,任务将被拒绝,并且使用 RejectedExecutionHandler.rejectEExecution() 方法拒绝新的任务
ThreadPoolExecutor 采取这样的思路是为了在执行 execute 方法时,避免获取全局锁,因为频繁获取全局锁会是一个严重的可伸缩问题,所以,几乎所有的 execute 方法调用都是通过执行步骤 2。
execute 方法
下面来通过源码来看一看。execute 方法源码如下:
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
//获取 ctl 的值
int c = ctl.get();
//如果线程池的线程个数少于corePoolSize则创建新线程执行当前任务
if (workerCountOf(c) < corePoolSize) {
//如果小于,增加工作队列,command 就是一个个的任务
if (addWorker(command, true))
//线程创建成功,直接返回
return;
//线程添加不成功,需要再次判断,每需要一次判断都会获取 ctl 的值
c = ctl.get();
}
//如果线程个数大于corePoolSize或者创建线程失败,则将任务存放在阻塞队列workQueue中
if (isRunning(c) && workQueue.offer(command)) {
//再次进行检查
int recheck = ctl.get();
//如果不是运行态并且成功的从阻塞队列中删除
if (! isRunning(recheck) && remove(command))
//执行拒绝策略
reject(command);
else if (workerCountOf(recheck) == 0)
// 增加工作线程
addWorker(null, false);
}
//如果当前任务无法放进阻塞队列中,则创建新的线程来执行任务
else if (!addWorker(command, false))
//执行拒绝策略
reject(command);
}
下图为 ThreadPoolExecutor 的 execute 方法的执行示意图:
addWorker 方法
从代码中可以看到 execute 中最关键的就是 addWorker 方法,而这个方法中两个参数的作用如下:
- 第一个参数是要执行的任务,如果为null那么会从等待队列中拉取任务
- 第二个参数是表示是否核心线程,用来控制 addWorker 方法流程的
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
// 死循环
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
//仅在必要时检查队列是否为空。
//线程池状态有五种,state 越小越是运行状态
//rs >= SHUTDOWN,表示此时线程池状态可能是 SHUTDOWN、STOP、TIDYING、TERMINATED
//默认 rs >= SHUTDOWN,如果 rs = SHUTDOWN,直接返回 false
//默认 rs < SHUTDOWN,是 RUNNING,如果任务不是空,返回 false
//默认 RUNNING,任务是空,如果工作队列为空,返回 false
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
//死循环
for (;;) {
//统计工作线程数量
int wc = workerCountOf(c);
//如果 worker 数量>线程池最大上限 CAPACITY(即使用int低29位可以容纳的最大值)
//或者 worker数量 > corePoolSize 或 worker数量>maximumPoolSize ),即已经超过了给定的边界
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
// 如果增加任务成功,退出该循环执行下面代码,否则继续
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
// 如果状态不等于之前获取的 state,跳出内层循环,继续去外层循环判断
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 {
//包装 Runnable 对象
//设置 firstTask 的值为 -1
//赋值给当前任务
//使用 worker 自身这个 runnable,调用 ThreadFactory 创建一个线程,并设置给worker的成员变量thread
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
//内置锁 加锁
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//在持有锁的时候重新检查
//如果 ThreadFactory 失败或在获得锁之前关闭,请回退
int rs = runStateOf(ctl.get());
// 判断线程池状态,防止使用过程中线程池被关闭
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
// 向正在被执行的任务队列workers中添加worker
// 注意区分
// HashSet<Worker> workers = new HashSet<Worker>() 线程池中线程
// private final BlockingQueue<Runnable> workQueue 等待被执行的任务
workers.add(w);
int s = workers.size();
// 记录任务最大数
if (s > largestPoolSize)
largestPoolSize = s;
// 添加任务成功
workerAdded = true;
}
} finally {
// 释放锁
mainLock.unlock();
}
// 添加任务成功,那么开始执行任务
if (workerAdded) {
// 重点代码 -- 我们需要查看worker中的run()
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
流程如下图:
addWorker 的作用有两个:增加工作线程数量和创建一个 Worker 并加到工作线程集合中。
worker 对象
Worker 位于 ThreadPoolExecutor 内部,它继承了 AQS 类并且实现了 Runnable 接口。Worker 类主要维护了线程运行过程中的中断控制状态。它提供了锁的获取和释放操作。
worker 对象的构造方法:
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
在Worker类的构造方法中需要做 3 个步骤:
- 初始 AQS 状态为 -1,此时不允许中断 interrupt(),只有在 worker 线程启动了,执行了 runWorker() 方法后,将 state 置为0,才能进行中断
- 将 firstTask 赋值给为当前类的全局变量
- 通过 ThreadFactory 创建一个新的线程
Worker 类实现了 Runnable 接口,需要重写 run 方法,而 Worker 的 run 方法本质上调用的是 ThreadPoolExecutor类的 runWorker 方法,在 runWorker 方法中,会首先调用 unlock 方法,该方法会将state置为0,所以这个时候调用shutDownNow 方法就会中断当前线程,而这个时候已经进入了 runWork 方法,就不会在还没有执行 runWorker 方法的时候就中断线程。
下面我们来看一下 runWorker 方法的源码:
runWorker方法
//循环从等待队列中获取任务并执行
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
// 获取worker对象中的任务 可以为null
Runnable task = w.firstTask;
w.firstTask = null;
//释放锁,将state设置为0,允许中断任务的执行
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
//循环调用 getTask() 获取任务,getTask()下文详细讲解
//获取到任务就执行,
//获取不到就阻塞等待新任务,
//返回null任务就销毁当前线程
while (task != null || (task = getTask()) != null) {
//如果任务不为空,则获取Worker工作线程的独占锁
w.lock();
//确保只有在线程 STOPING 时,才会被设置中断标志,否则清除中断标志。
//如果一开始判断线程池状态 < STOPING,但 Thread.interrupted() 为 true,
//即线程已经被中断,又清除了中断标示,再次判断线程池状态是否 >= stop
//是,再次设置中断标示,wt.interrupt()
//否,不做操作,清除中断标示后进行后续步骤
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
//执行任务前执行的逻辑
beforeExecute(wt, task);
Throwable thrown = null;
try {
//调用Runable接口的run方法执行任务
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;
//完成的任务数量加1
w.completedTasks++;
//释放工作线程获得的锁
w.unlock();
}
}
completedAbruptly = false;
} finally {
// 如果到这里,需要销毁线程:
// 1. getTask 返回 null退出while循环,队列中没有任务或空闲线程超时
// 2. 任务执行过程中发生了异常
processWorkerExit(w, completedAbruptly);
}
}
下面是 runWorker 的执行流程图如下:
从代码分析上可以看到,当从 Worker 线程中获取的任务为空时,会调用 getTask() 方法从任务队列中获取任务,接下来,我们看下getTask()方法的实现。
getTask方法
private Runnable getTask() {
//判断是否超时
boolean timedOut = false;
//死循环
for (;;) {
//获取ctl
int c = ctl.get();
//获取线程池的状态
int rs = runStateOf(c);
//若当前线程池的工作状态为RUNNING则不会进入下面if。
//若状态为STOP、TIDYING、TERMINATED则当前工作线程不能执行任务。
//若状态为SHUTDOWN,且阻塞队列为空,则获取任务为null
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
//减少工作线程的数量
decrementWorkerCount();
return null;
}
// 获取工作线程的数量
int wc = workerCountOf(c);
// 判断工作线程是否需要剔除
// 如果允许核心线程超时执行或者工作线程数大于核心线程数就标识timed为true
// 当timed为true时:
// 允许我们在超时时间内获取任务,当阻塞队列为空时,等待指定的时间后如果无法获取任务就返回null
// 当timed为false时:此时取任务会阻塞,直到阻塞队列中有了新任务
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
//如果工作线程的数量大于 maximumPoolSize 会进行线程剔除
//如果使用了 allowCoreThreadTimeOut ,并且工作线程不为0或者队列有任务的话,会直接进行线程剔除
if ((wc > maximumPoolSize || (timed && timedOut)) && (wc > 1 || workQueue.isEmpty())) {
//成功减少线程池中的工作线程数量
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
//从任务队列中获取任务
Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take();
//任务不为空直接返回任务
if (r != null)
return r;
// 如果获取到空任务,就将超时设置为true,下一轮就有可能会超时阻塞
timedOut = true;
} catch (InterruptedException retry) {
//如果阻塞等待过程中线程发生中断,则将timeOut设置为false,进入下一次循环
timedOut = false;
}
}
processWorkerExit方法
工作线程退出是 runWorker 的最后一步,这一步会判断工作线程是否突然终止,并且会尝试终止线程,以及是否需要增加线程来替换原工作线程。
private void processWorkerExit(Worker w, boolean completedAbruptly) {
//completedAbruptly 是 true,突然终止,说明是 task 执行时异常情况导致,即run()方法执行时发生了异常,那么正在工作的 worker 线程数量需要-1
//completedAbruptly 是 false 是突然终止,说明是 worker 线程没有 task 可执行了,不用-1,因为已经在 getTask() 方法中-1了
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
//将工作线程的数量减1
decrementWorkerCount();
//获取锁并上锁
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//全局的已完成任务记录数加上此将要终结的Worker中的已完成任务数
completedTaskCount += w.completedTasks;
//工作线程集合中移除此将要终结的Worker
workers.remove(w);
} finally {
//释放锁
mainLock.unlock();
}
//见下一小节分析,用于尝试终止线程池
tryTerminate();
//获取ctl
int c = ctl.get();
//如果线程池的状态小于STOP,也就是处于RUNNING或者SHUTDOWN状态的前提下:
//1.如果线程不是由于抛出用户异常终结,如果允许核心线程超时,则保持线程池中至少存在一个工作线程
//2.如果线程由于抛出用户异常终结,或者当前工作线程数,那么直接添加一个新的非核心线程
if (runStateLessThan(c, STOP)) {
if (!completedAbruptly) {
// 如果允许核心线程超时,最小值为0,否则为corePoolSize
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
// 如果最小值为0,同时任务队列不空,则更新最小值为1
if (min == 0 && ! workQueue.isEmpty())
min = 1;
// 工作线程数大于等于最小值,直接返回不新增非核心线程
if (workerCountOf(c) >= min)
return; // replacement not needed
}
addWorker(null, false);
}
}
接下来,我们看下tryTerminate()方法。
tryTerminate方法
final void tryTerminate() {
//自旋for循环
for (;;) {
//获取ctl
int c = ctl.get();
//如果线程池的状态为RUNNING
//或者状态大于TIDYING
//或者状态为SHUTDOWN并且任务队列为空
//直接返回程序,不再执行后续逻辑
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
return;
//如果当前线程池中的线程数量不等于0
if (workerCountOf(c) != 0) {
//中断线程的执行
interruptIdleWorkers(ONLY_ONE);
return;
}
//获取线程池的全局锁
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//通过CAS将线程池的状态设置为TIDYING
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
try {
//调用terminated()方法
terminated();
} finally {
//将线程池状态设置为TERMINATED
ctl.set(ctlOf(TERMINATED, 0));
//唤醒所有因为调用线程池的 awaitTermination 方法而被阻塞的线程
termination.signalAll();
}
return;
}
} finally {
//释放锁
mainLock.unlock();
}
}
}
关闭线程池
关闭线程池使用 shutdown 方法或 shutdownNow 方法,最终目的是将线程池状态设置成 TERMINATED。
shutdown 方法:
shutdown()方法可以安全地关闭一个线程池,调用 shutdown() 方法之后线程池并不是立刻就被关闭,因为这时线程池中可能还有很多任务正在被执行,或是任务队列中有大量正在等待被执行的任务,调用 shutdown() 方法后线程池会在执行完正在执行的任务和队列中等待的任务后才彻底关闭。
调用 shutdown() 方法后如果还有新的任务被提交,线程池则会根据拒绝策略直接拒绝后续新提交的任务。
public void shutdown() {
//获取线程池的全局锁
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//检测是否有关闭线程池的权限
checkShutdownAccess();
//将线程池状态设置为SHUTDOWN态
advanceRunState(SHUTDOWN);
//中断空闲线程(没有执行任务的线程)
interruptIdleWorkers();
//该方法在ThreadPoolExecutor中是一个空方法
onShutdown();
} finally {
//释放锁
mainLock.unlock();
}
// 尝试结束线程池
tryTerminate();
}
checkShutdownAccess 用于检查当前线程是否有访问所有工作线程的权限,如果没有的话可能会抛出异常
private void checkShutdownAccess() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkPermission(shutdownPerm);
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers)
// 这个方法可能抛出异常
security.checkAccess(w.thread);
} finally {
mainLock.unlock();
}
}
}
tryTerminate()方法
结束线程池,最终将线程池状态设置为 TERMINATED
final void tryTerminate() {
for (;;) {
int c = ctl.get();
//当前线程池的状态为以下几种情况时,直接返回:
//RUNNING,因为还在运行中,不能停止;
//TIDYING或TERMINATED,已经关闭了;
//SHUTDOWN并且等待队列非空,这时要执行完workQueue中的task;
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
return;
// 如果线程数量不为0,则中断一个空闲的工作线程,并返回
if (workerCountOf(c) != 0) { // Eligible to terminate
interruptIdleWorkers(ONLY_ONE);
return;
}
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 尝试设置状态为TIDYING
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
try {
// 钩子方法,留给子类实现
terminated();
} finally {
// 设置状态为TERMINATED
ctl.set(ctlOf(TERMINATED, 0));
termination.signalAll();
}
return;
}
} finally {
mainLock.unlock();
}
}
}
shutdownNow方法:
shutdownNow() 方法和 shutdown() 方法的区别就是多了一个 Now,表示立刻关闭的意思,不推荐使用这一种方式关闭线程池。
如果调用了 shutdownNow 方法,则线程池将不再接收新的执行任务,也会将任务队列中存在的任务丢弃,正在执行的 worker 线程也会立即中断,同时该方法会立即返回,返回当前任务队列中被丢弃的任务列表
public List shutdownNow() {
List tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 检查访问权限
checkShutdownAccess();
// CAS设置线程池状态为STOP
advanceRunState(STOP);
// 中断所有工作线程,无论是否空闲
interruptWorkers();
// 取出阻塞队列中没有被执行的任务并返回
tasks = drainQueue();
} finally {
//释放锁
mainLock.unlock();
}
// 结束线程池,最终将线程池状态设置为TERMINATED
tryTerminate();
return tasks;
}
流程如下:
- 将线程池切换到 STOP 状态
- 中断所有工作线程,无论是否空闲
- 取出阻塞队列中没有被执行的任务并返回
- 调用 tryTerminate 尝试结束线程池
shutdown 和 shutdownNow 的区别
- shutdown 会等待线程池中的任务执行完成之后关闭线程池,而 shutdownNow 会给所有线程发送中断信号,中断任务执行,然后关闭线程池
- shutdown 没有返回值,而 shutdownNow 会返回关闭前任务队列中未执行的任务集合(List)
总结
线程池执行的流程: