来自AI的总结:使用 ThreadPoolExecutor
的好处包括:通过线程复用降低创建和销毁线程的开销,控制并发线程数量防止资源耗尽,提高任务响应速度,统一管理任务执行,支持灵活的任务队列和拒绝策略,增强系统稳定性,简化多线程编程,并适应多种任务场景,从而提升性能、资源利用率和开发效率。
1 ThreadPoolExecutor 构造方法
1.1 创建线程池
public class ThreadPoolTest {
public static void main(String[] args) {
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(
// 核心线程数
3,
// 最大线程数
5,
5,
TimeUnit.MINUTES,
// 初始化容量为 10 的有界阻塞队列
new LinkedBlockingQueue<>(10),
// 默认的线程构建工厂
Executors.defaultThreadFactory(),
// 匿名内部类重写 拒绝策略
new ThreadPoolExecutor.CallerRunsPolicy() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
System.out.println("当前线程为 : " + Thread.currentThread().getName()+ " | 线程池已满, 该任务已被拒绝 | 核心线程数 : "
+ e.getCorePoolSize() + " | 总线程数 : " + e.getMaximumPoolSize() + " | 阻塞队列任务数 : " + e.getQueue().size());
// 直接调用run方法,表示由创建线程池的线程来执行任务,此处我们不需要执行
// r.run();
}
});
// 此处我们提交 20 个任务
for (int i = 0; i < 20; i++) {
poolExecutor.submit(() -> {
try {
System.out.println(Thread.currentThread().getName() + " | 正在运行");
// 睡眠 5 分钟模拟运行业务逻辑
TimeUnit.MINUTES.sleep(5);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
}
}
}
- 运行结果:
当前线程为 : main | 线程池已满, 该任务已被拒绝 | 核心线程数 : 3 | 总线程数 : 5 | 阻塞队列任务数 : 10
pool-1-thread-2 | 正在运行
pool-1-thread-1 | 正在运行
pool-1-thread-3 | 正在运行
当前线程为 : main | 线程池已满, 该任务已被拒绝 | 核心线程数 : 3 | 总线程数 : 5 | 阻塞队列任务数 : 10
pool-1-thread-5 | 正在运行
pool-1-thread-4 | 正在运行
当前线程为 : main | 线程池已满, 该任务已被拒绝 | 核心线程数 : 3 | 总线程数 : 5 | 阻塞队列任务数 : 10
当前线程为 : main | 线程池已满, 该任务已被拒绝 | 核心线程数 : 3 | 总线程数 : 5 | 阻塞队列任务数 : 10
当前线程为 : main | 线程池已满, 该任务已被拒绝 | 核心线程数 : 3 | 总线程数 : 5 | 阻塞队列任务数 : 10
- 通过上面的结果,我们可以发现一共 20 个任务,正在执行的任务有 5 个,10 个在阻塞队列 LinkedBlockingQueue 中,还有 5 个任务被拒绝掉了;最大线程数(5) + 阻塞队列中的任务数(10) + 被拒绝的任务数(5) = 总任务数(20);
1.2 核心参数
- corePoolSize:核心线程池的大小;
- maximumPoolSize:最大线程池大小;
- keepAliveTime:线程池中超过 corePoolSize 数的空闲线程最大存活时间;allowCoreThreadTimeOut(true) 方法可以使核心线程超出有效时间也被关闭掉;
- TimeUnit keepAliveTime: keepAliveTime 的时间单位;
- workQueue:存放超出核心线程数的任务的阻塞队列,包括有界和无界队列;
- LinkedBlockingQueue:包括创建有界和无界队列的构造方法;
- ArrayBlockingQueue:只能创建有界的队列;
- threadFactory:创建线程的工厂;
- RejectedExecutionHandler:当提交任务数超过 最大线程数 + 有界队列长度 之和时,任务会交给 RejectedExecutionHandler 来处理;
- CallerRunsPolicy:若线程池未关闭,则调用run方法,也就是创建线程池的线程来执行任务;
- AbortPolicy:直接抛出 rejectedExecution 异常;此为默认拒绝策略;
- DiscardPolicy:什么也不做;
- DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务;
1.2.1 corePoolSize,
maximumPoolSize,workQueue 和任务数之间的关系
- 线程池中线程数 < corePoolSize,提交任务到线程池时务会新建一个线程执行该任;
- 线程池中线程数 > corePoolSize
,
提交任务到线程池时会将任务添加到 workQueue 中; - workQueue 中任务数 == 有界队列长度,且 maximumPoolSize > corePoolSize ,新提交的任务时会新建一个线程执行该任;
- 当任务数 > maximumPoolSize,则会使用拒绝策略执行任务;
1.3 创建线程池的其他方式
// 创建可缓存的线程池,池中线程数量根据任务数智能调节
ExecutorService cachedPool = Executors.newCachedThreadPool();
// 创建固定大小的线程池
ExecutorService fixedPool = Executors.newFixedThreadPool(5);
// 创建只有 1 个线程的线程池
ExecutorService singlePool = Executors.newSingleThreadExecutor();
但是阿里开发手册不允许使用这三种方式,原因是这三种创建方式定义的队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
2 线程池提交任务
2.1 ThreadPoolExecutor 的架构
2.2 ThreadPoolExecutor 重要参数
public class ToBinary {
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// runState is stored in the high-order bits
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
private static int runStateOf(int c) { return c & ~CAPACITY; }
private static int workerCountOf(int c) { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }
public static void main(String[] args) {
System.out.println("CAPACITY | " + toBinary(CAPACITY) + " | " + CAPACITY);
System.out.println("RUNNING | " + toBinary(RUNNING) + " | " + RUNNING);
System.out.println("SHUTDOWN | " + toBinary(SHUTDOWN) + " | " + SHUTDOWN);
System.out.println("STOP | " + toBinary(STOP) + " | " + STOP);
System.out.println("TIDYING | " + toBinary(TIDYING) + " | " + TIDYING);
System.out.println("TERMINATED | " + toBinary(TERMINATED) + " | " + TERMINATED);
}
public static String toBinary(int num) {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < 32; i++) {
builder.append((num & 1 << i) == 0 ? '0' : '1');
}
return builder.reverse().toString();
}
}
- 运行结果:
CAPACITY | 00011111111111111111111111111111 | 536870911
RUNNING | 11100000000000000000000000000000 | -536870912
SHUTDOWN | 00000000000000000000000000000000 | 0
STOP | 00100000000000000000000000000000 | 536870912
TIDYING | 01000000000000000000000000000000 | 1073741824
TERMINATED | 01100000000000000000000000000000 | 1610612736
- 参数 ctl 的高3位用来表示 线程池的运行状态 runState ,低29位用来表示线程池中的线程数量 workerCount;
- CAPACITY:高3位为 0 ,低29位为 1 ,表示线程池中的线程数量最大为 536870911;
- RUNNING:高3位为 1 ,低29位为 0 ,该状态的线程池会接收新任务,也会处理在阻塞队列中等待处理的任务;
- SHUTDOWN:32位都为 0 ,该状态的线程池不会再接收新任务,但还会处理已经提交到阻塞队列中等待处理的任务;
- STOP:高3位为 001 ,低29位为 0 ,该状态的线程池不会再接收新任务,不会处理在阻塞队列中等待的任务,而且还会中断正在运行的任务;
- TIDYING:高3位为 010 ,低29位为 0 ,该状态下所有任务都被终止了,还将调用terminated() 方法;
- TERMINATED:高3位为 011 ,低29位为 0 ,terminated()方法调用完成后变成此状态;
- runStateOf 方法:与高3位为 1 ,低29位为 0 进行 & 操作,用来获取 ctl 参数的高3位保存的线程池状态;
- workerCountOf 方法:与高3位为 0,低29位为 1 进行 & 操作,用来获取 ctl 参数的 低29位保存的当前线程数;
- RUNNING < SHUTDOWN < STOP < TIDYING < TERMINATED
2.3 submit 方法重载
// 提交任务为 Runnable 无法获取到执行结果
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
// 将 Runnable 和 result 封装为 RunnableAdapter
// RunnableAdapter 又实现于 Callable
// 又将 RunnableAdapter 封装为 FutureTask 调用 execute
// 入参为 Runnable 也能获取到结果
public <T> Future<T> submit(Runnable task, T result) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task, result);
execute(ftask);
return ftask;
}
// 将 Callable 封装为 FutureTask 调用 execute
// 可以获取到结果
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
2.4 execute 方法
execute 方法包含了 添加线程到线程池、当没有空闲线程时将任务添加到阻塞队列或拒绝掉、添加线程成功后启动执行当前任务、添加到线程池的线程自旋从阻塞队列中获取任务并执行、并根据 allowCoreThreadTimeOut 参数判断当出现空闲线程并超时后是否移除核心线程或最大线程等核心逻辑。
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
// 获取 ctl 的值
int c = ctl.get();
// 若 线程池中线程数量 < corePoolSize(核心线程数量)
if (workerCountOf(c) < corePoolSize) {
// 添加新的线程到线程池,执行该任务
if (addWorker(command, true))
return;
c = ctl.get();
}
// 线程池中线程数量 > corePoolSize(核心线程数量) 或 添加任务失败
// 若线程池为 RUNNING(运行态),并且添加任务到 阻塞队列 成功
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
// 再次检查 运行状态
// 若已处于 非RUNNING 状态, (就不应该再接收任务)且移除当前任务成功,则调用 拒绝策略
if (! isRunning(recheck) && remove(command))
reject(command);
// 处于 RUNNING (运行态) 且 线程池中没有线程,则添加一个 空任务的 线程到线程池
// 或处于 非RUNNING 状态 且 移除当前任务失败 且 线程池中没有线程,则添加一个 空任务的 线程到线程池
// 保证有一个线程处理任务
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
// 若状态 不为RUNNING(运行态),或者添加阻塞队列失败
// 再次添加线程,若添加失败,则调用 拒绝策略
else if (!addWorker(command, false))
reject(command);
}
execute 执行流程:
- 线程池中线程数量 < corePoolSize(核心线程数量) ,添加新的线程到线程池,执行该任务,addWorker 失败的原因:
- 线程池状态已超过SHUTDOWN;
- 线程池状态为SHUTDOWN ,但当前提交的任务不为空;
- 线程池状态为SHUTDOWN ,当前提交的任务为空,阻塞队列里的任务为空;
- 上面三种情况都未满足,但线程池线程数量超过了 maximumPoolSize 或 corePoolSize;
- 线程池中线程数量 > corePoolSize(核心线程数量) 或 添加任务失败后,若线程池为 RUNNING(运行态),并且添加任务到 阻塞队列 成功
- 再次检查线程池状态,若已处于 非RUNNING 状态,(就不应该再接收任务)且移除当前任务成功,则调用 拒绝策略;
- 处于 RUNNING (运行态) 且 线程池中没有线程,则添加一个 空任务的 线程到线程池;
或处于 非RUNNING 状态 且 移除当前任务失败 且 线程池中没有线程,则添加一个 空任务的 线程到线程池;保证有一个线程处理任务;
- 若状态 不为RUNNING(运行态),或者添加阻塞队列失败(说明队列已满);再次添加线程,若添加失败,则调用 拒绝策略;
2.4.1 addWorker 方法
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// 以下三种情况会 直接返回 false
// 线程池状态已超过SHUTDOWN;
// 线程池状态为SHUTDOWN ,但当前提交的任务不为空;
// 线程池状态为SHUTDOWN ,当前提交的任务为空,阻塞队列里的任务为空;
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
// 获取线程池中的线程数量
int wc = workerCountOf(c);
// 线程池线程数量超过了 maximumPoolSize 或 corePoolSize,也返回 false
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
// 使用 unSafe 将 ctl +1,也就是线程数 +1
// 若 +1 成功,则跳出大循环
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
// 再次比较线程池状态
// 若不相等,说明线程池状态已发生改变,需再次到外循环判断
// 若相等,说明线程池状态未改变,只是有其他线程修改了 workerCount,需到内循环继续判断
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的AQS锁的同步状态state为-1,防止被中断
// 调用线程工厂创建线程
w = new Worker(firstTask);
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());
// 线程池状态为 RUNNING
// 或 (线程池状态为 SHUTDOWN 且 当前任务为空)
// 则 将线程添加到线程池,设置 largestPoolSize 为当前线程数量,设置 workerAdded 成功
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;
workerAdded = true;
}
} finally {
// 释放锁
mainLock.unlock();
}
// 若添加线程成功,则启动线程执行任务
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
// 若启动线程失败,则将线程 remove,并将线程数量 -1
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
addWorker 执行流程:
- 通过当前线程池状态 runState 判断是否可以添加线程到线程池:以下三种直接返回 false
- 线程池状态已超过SHUTDOWN;
- 线程池状态为SHUTDOWN ,但当前提交的任务不为空;
- 线程池状态为SHUTDOWN ,当前提交的任务为空,阻塞队列里的任务为空;
- runState 判断可以添加,再通过 workerCount 判断是否可以添加线程到线程池:
- 线程池线程数量超过了 maximumPoolSize 或 corePoolSize 返回false;
- 使用 unSafe 设置 ctl +1,表示线程数量 +1;
- 使用 ReentrantLock 加锁向线程池中添加线程,若添加成功则启动线程;若添加失败则执行 addWorkerFailed,将线程 remove,并将线程数量 -1;
2.4.2 runWorker 方法
final void runWorker(ThreadPoolExecutor.Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
// 调用Worker类的tryRelease()方法,将state设置为0,使得当前线程允许中断
w.unlock(); // allow interrupts
// 是否为 突然完成,若是异常导致的进入 finally, completedAbruptly 就会为 true
boolean completedAbruptly = true;
try {
// 若当前任务不为空,或者从阻塞队列获取任务不为空
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
// 确保只有 runState >= STOP,线程才会被设置中断标识
// 若 runState >= STOP, 且中断标识 wt.isInterrupted() 为 false,则 wt.interrupt(),设置中断标识
// 若 runState < STOP, Thread.interrupted() 判断是否中断并且设置中断标识为 false;
// 若Thread.interrupted() 为 true,再次判断 runState
// 若 runState >= STOP,由于 Thread.interrupted() 设置中断标识为 false,则 wt.interrupt(),设置中断标识;
// 若Thread.interrupted() 为 false,无后序判断;
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 = null;
// 当前线程完成任务数 +1
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
// 当获取不到任务 且 获取任务超时 或 出现异常,当前worker会进入退出流程
processWorkerExit(w, completedAbruptly);
}
}
runWorker 执行流程:
- 当前线程启动后,先调用 unlock 方法将 state 设置为 0,允许当前线程中断;
- 当 runState >= STOP,则设置当前线程中断标识为 true,否则重置中断标识为 false;
- 执行 beforeExecute,task.run,afterExecute 方法
- 若抛出异常 或 Error,则执行线程退出逻辑 processWorkerExit;
- 若正常执行,则将完成任务数 +1;
- 若 while 循环超时获取不到任务,也执行线程退出逻辑 processWorkerExit;
2.4.3 getTask 方法
getTask 方法通过线程池状态、获取任务超时,获取任务被挂起后被中断抛出的异常等方法来控制当前线程的退出。
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.
// 若线程池状态 >= STOP 或
// 若线程池状态 == SHUTDOWN 且 阻塞队列 为空
// 则使用 unSafe 将线程数量 -1,也就是 ctl -1,并返回 null,会使得当前线程退出
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
// 获取当前线程数
int wc = workerCountOf(c);
// Are workers subject to culling?
// 若 allowCoreThreadTimeOut 为 true,核心线程在获取任务超时后,也会被回收
// timed 不论是 核心线程 或是 最大线程 都为 true;都会调用 workQueue.poll 方法获取任务,当超时后都会被移除
// 若 allowCoreThreadTimeOut 为 false
// 当前线程为 核心线程,timed为 false;调用 workQueue.take 获取任务,此方法会一直阻断,直到被唤醒,所以和线程不会被移除
// 当前线程为 非核心线程,timed为 true;会调用 workQueue.poll 方法获取任务,当超时后都会被移除
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
// 若当前线程数 > maximumPoolSize,超出最大线程数,直接将此线程移除
// 当前线程数 <= maximumPoolSize 且
// allowCoreThreadTimeOut 为 true,核心线程和最大线程调用 workQueue.poll 获取任务,且都可能超时
// 若超时,并且满足(当前线程 > 1 或 阻塞队列为空),核心线程或最大线程数将 -1,并退出
// 若未超时,则调用 workQueue.poll 来获取任务,并返回获取的任务
// allowCoreThreadTimeOut 为 false,核心线程 获取任务 workQueue.take 会一直阻断,直到被唤醒,最大线程获取任务 workQueue.poll 可能超时
// 只有最大线程会超时,并且满足(当前线程 > 1 或 阻塞队列为空),最大线程将-1,并退出
// 若未超时,核心线程调用 workQueue.take 来获取任务,队列为空则一直阻断;最大线程调用 workQueue.poll 来获取任务,并返回获取的任务;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
// workQueue.poll, 使用 LockSupport.parkNanos(this, nanosTimeout) 阻断固定时间
// workQueue.take,使用 LockSupport.park(this) 一直阻断,直到被唤醒
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
getTask 执行流程:
- 根据线程池状态 runState 判断是否返回null,以下两种将返回null,将 ctl -1,会使得当前线程退出;
- 若线程池状态 >= STOP;
- 若线程池状态 == SHUTDOWN 且 阻塞队列 为空;
- 若当前线程数 > maximumPoolSize,将返回null,将 ctl -1,会使得当前线程退出;
- 当前线程数 <= maximumPoolSize 且
- allowCoreThreadTimeOut 为 true,核心线程和最大线程调用 workQueue.poll 获取任务,且都可能超时;
- 若超时,并且满足(当前线程 > 1 或 阻塞队列为空),核心线程或最大线程数将 -1,并在 processWorkerExit 方法中被移除当前线程;
- 若未超时,则都调用 workQueue.poll 来获取任务,并返回获取的任务;
- allowCoreThreadTimeOut 为 false,核心线程 调用 workQueue.take 获取任务,会一直阻断,直到被唤醒,不会超时,所以核心线程不会被回收;最大线程调用 workQueue.poll 获取任务,可能超时;
- 只有最大线程会超时,并且满足(当前线程 > 1 或 阻塞队列为空),最大线程将 -1,并在 processWorkerExit 方法中被移除当前线程;
- 若未超时,核心线程调用 workQueue.take 来获取任务,若队列为空,一直阻断,直到被唤醒;最大线程调用 workQueue.poll 来获取任务,并返回获取的任务;
- allowCoreThreadTimeOut 为 true,核心线程和最大线程调用 workQueue.poll 获取任务,且都可能超时;
2.4.4 processWorkerExit 方法
private void processWorkerExit(ThreadPoolExecutor.Worker w, boolean completedAbruptly) {
// completedAbruptly 为 true,表示是由于异常等情况导致线程退出,要将 ctl -1
// completedAbruptly 为 false,表示是由于线程池状态,线程数量不符合,或者获取任务超时导致的退出,已经在 getTask 中 -1
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 将当前线程完成的任务数加到线程池的完成任务数量中
completedTaskCount += w.completedTasks;
// 移除当前线程
workers.remove(w);
} finally {
mainLock.unlock();
}
// 尝试终止线程池
// 若满足终止状态,但还有线程,尝试将其中断
// 若满足终止状态,且没有线程,先将状态置为 TIDYING,执行 terminated 后,再将状态置为 TERMINATED
tryTerminate();
int c = ctl.get();
// 若当前线程状态处于 STOP 之前
if (runStateLessThan(c, STOP)) {
// 线程正常退出
if (!completedAbruptly) {
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
// allowCoreThreadTimeOut 为 true,且阻塞队列不为空,要保证至少一个线程存在
// allowCoreThreadTimeOut 为 false,若线程数 >= 核心线程数,则返回;若线程数 < 核心线程数,则添加一个空任务的线程
if (min == 0 && ! workQueue.isEmpty())
min = 1;
if (workerCountOf(c) >= min)
return; // replacement not needed
}
// 只要是异常退出或者少于要保持的线程数,都将会添加一个空任务的线程
addWorker(null, false);
}
}
processWorkerExit 执行流程:
- completedAbruptly 为 true,表示是由于异常等情况导致线程退出,要将 ctl -1;completedAbruptly 为 false,表示是由于线程池状态,线程数量不符合,或者获取任务超时导致的退出,ctl 无需 -1;
- 将当前线程完成的任务数加到线程池的完成任务数量中并移除当前线程;
- 尝试终止线程池;
- 若当前线程状态处于 STOP 之前,RUNNING 或 SHUTDOWN
- 线程正常退出,completedAbruptly 为 false
- allowCoreThreadTimeOut 为 true 且 阻塞队列不为空,则保证至少有一个线程存在;
- allowCoreThreadTimeOut 为 false,线程数 >= 核心线程数,则返回;若线程数 < 核心线程数,则添加一个空任务的线程;
- 线程正常退出,completedAbruptly 为 false
3 关闭线程池
线程池关闭有两个方法:shutdown 和 shutdownNow 两个方法,方法区别为:
- shutdown 方法将线程池状态改为 SHUTDOWN,且将空闲线程打上中断标识,尝试关闭线程池,当阻塞队列中的任务都被执行完后,线程池中的线程才会全部移除;
- shutdownNow 方法将线程池状态改为 STOP,将所有未中断的线程打上中断标识,并清空阻塞队列,再尝试关闭线程池,所有线程都将移除,强制关闭线程池;
3.1 shutdown方法
优雅关闭线程池:
ExecutorService executor = Executors.newFixedThreadPool(5);
// 提交任务
executor.submit(() -> {
// 任务逻辑
});
// 启动关闭过程
executor.shutdown();
try {
// 等待线程池终止,最多等待1小时
if (!executor.awaitTermination(1, TimeUnit.HOURS)) {
// 如果超时,强制关闭线程池
executor.shutdownNow();
}
} catch (InterruptedException e) {
// 处理中断异常
executor.shutdownNow();
}
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 检查当前线程是否有权限关闭线程池或中断工作线程
checkShutdownAccess();
// 将线程池状态修改为 SHUTDOWN
advanceRunState(SHUTDOWN);
// 将空闲状态下(可能在获取任务,包括核心线程)的线程打上中断标识;
interruptIdleWorkers();
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
// 尝试关闭线程池
tryTerminate();
}
3.2 shutdownNow 方法
直接关闭线程池:
ExecutorService executor = Executors.newFixedThreadPool(5);
// 提交任务
executor.submit(() -> {
// 任务逻辑
});
// 立即关闭线程池
List<Runnable> notExecutedTasks = executor.shutdownNow();
// 处理未执行的任务
for (Runnable task : notExecutedTasks) {
// 处理未执行的任务
}
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;
}
final void tryTerminate() {
for (;;) {
int c = ctl.get();
// 线程池还在运行状态
// 线程池状态至少为 TIDYING,说明已经被关闭了
// 线程池状态为 SHUTDOWN 并且 阻塞队列还有任务,直接返回,还不满足关闭条件或已关闭
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
return;
// 满足关闭条件,但是还有线程,则中断工作线程集合中的第一个空闲的工作线程
if (workerCountOf(c) != 0) { // Eligible to terminate
interruptIdleWorkers(ONLY_ONE);
return;
}
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// CAS设置线程池状态为TIDYING,如果设置成功则执行terminated
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
try {
terminated();
} finally {
// 更新线程池状态为TERMINATED
ctl.set(ctlOf(TERMINATED, 0));
// 当线程池完全终止(即所有工作线程退出且任务队列为空)时,调用 termination.signalAll() 唤醒所有正在等待线程池终止的线程
termination.signalAll();
}
return;
}
} finally {
mainLock.unlock();
}
// else retry on failed CAS
}
}