本文章阅读需要简单阅读过ThreadPoolExecutor
源码,或对其八股熟悉
1、八股结论
keepAliveTime
:线程池中的线程数量大于 corePoolSize
的时候,如果这时没有新的任务提交,多余的空闲线程不会立即销毁,而是会等待,直到等待的时间超过了 keepAliveTime
才会被回收销毁,线程池回收线程时,会对核心线程和非核心线程一视同仁,直到线程池中线程的数量等于 corePoolSize
,回收过程才会停止。
原文链接:javaguide.cn/java/concur…
2、调试Demo
package org.example.TestThreadPool;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* ---leetcode---
*
* @author summer77
* @date 2024/3/2 16:07
* <p>
* ---TestThreadPool---
*/
public class TestThreadPool {
public static void main(String[] args) {
// 核心线程数量为3,非核心线程数量最多为2,任务队列最多存放2个任务
ThreadPoolExecutor executor = new ThreadPoolExecutor(
3, // 核心线程数量
5, // 最大线程数量
60, // 非核心线程空闲时可存活时间
TimeUnit.SECONDS, // 单位
new ArrayBlockingQueue<>(2), // 阻塞任务队列大小
new ThreadPoolExecutor.AbortPolicy()
);
for (int i = 0; i < 7; i++) {
int finalI = i + 1;
executor.execute(()->{
System.out.println("任务" + finalI + "正在执行...");
});
}
}
}
3、深入源码
3.1、execute()
- 针对非核心线程,核心线程的创建我们就不细看了
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
// 当工作线程数量小于核心线程时,正常创建新的核心线程,不讨论这里
if (workerCountOf(c) < corePoolSize) {
// 注意第二个参数 true,他决定着非核心线程的创建
if (addWorker(command, true))
return;
c = ctl.get();
}
// 走到这里说明核心线程满了,无法再继续创建核心线程了,这时会将任务添加到任务队列
// 如果当前线程池仍然是运行态,将任务添加到任务队列中,如果队列已满,会返回false
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
// 如果不是运行态则移除刚刚放入阻塞队列的任务并调用饱和策略
// 这个remove()方法中还会tryTerminate()
if (! isRunning(recheck) && remove(command))
reject(command);
// 如果工作线程数量为0,则创建一个工作线程
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
// 到这里才是非核心线程的创建
// 我们的探索的是任务队列已满才来到这里的情况
// 注意第二个参数false,他决定了非核心线程的创建
else if (!addWorker(command, false))
// 添加失败,代表线程池状态不允许新任务添加,或线程数达到了最大线程数
// 调用饱和策略
reject(command);
}
3.2、非核心线程的创建——addWorker(task,false)
假定我们这次创建的是非核心线程,参数为false
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 ||
// 注意这个core,就是我们传入的参数false
// core为true,判断逻辑为 wc >= corePoolSize,如果大于核心线程数就直接return false了
// core为false,判断逻辑为wc >= maximumPoolSize,如果大于最大线程数return false
// 如果当前小于maximumPoolSize,我们就有机会创建非核心线程了
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
// 能走到这里说明满足新线程创建条件了。
// ctl低位+1 (也就是工作线程数+1)
// 如果自增失败,代表有竞争,检查后重试即可。
// 如果自增成功,则break
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
}
}
// 剩下的就是比较常规的创建工作线程,但是非核心线程可以被创建出来了
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
// 创建一个worker,并指定第一个要执行的任务
w = new Worker(firstTask);
// 获取Worker中新建的线程
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());
// 再次检查运行状态
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
// 检查线程存活状态
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
// 将worker加入工作线程集合,这是一个HashSet
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
//记录线程池中的最大工作线程数,可用于观测线程池运行情况
largestPoolSize = s;
// 加入成功后标识为true
workerAdded = true;
}
} finally {
mainLock.unlock();
}
// 如果工作线程创建添加成功,start线程
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w); //如果线程启动失败,这里会从workers中移除添加的worker,并且尝试终止线程池
}
return workerStarted; //返回值即可看作线程是否创建、添加、启动全部成功
}
接下来就是探寻工作线程如何执行任务
3.3、runWorker()
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
// 这里获取的是第一次创建Worker时分配的第一个任务,将他存在了task上
Runnable task = w.firstTask;
//重置为null,为后续循环获取新的任务做准备
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
//如果初始任务还没执行,先进入循环块执行初始任务。如果执行过了,则循环调用getTask()方法,获取新的任务
//我们主要关注点是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 = null; //task设为空,保证循环获取新任务
w.completedTasks++; //记录已经执行的数量
w.unlock(); //解锁
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly); //如果被打断了会把线程数量减到0
}
}
3.4、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.
//如果线程池状态为SHUTDOWN且任务队列为空,或线程池已经处于不处理新任务的更高结束态
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
// 将线程数量减到0 ,操作的是ctl,实际线程此时还没有被销毁
decrementWorkerCount();
return null;
}
// 获取线程数量
int wc = workerCountOf(c);
// Are workers subject to culling?
// 重点开始了
// allowCoreThreadTimeOut默认值是false,它是通过allowCoreThreadTimeOut()方法去改变的,一会再说实际作用
// 我们这里先看allowCoreThreadTimeOut为false的情况
// 如果当前工作线程数已经大于核心线程数,timed为true
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
// 第一次来timeOut是false,暂时不会进入下面这个if块
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
// 尝试从任务队列中获取任务
// 此时timed为true,执行的是poll方法
Runnable r = timed ?
// 参数:keepAliveTime
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take(); //无救急线程时调用,直接返回
if (r != null)
return r; //执行成功则直接return
timedOut = true; //超时,没拿到任务,救急线程太闲了该死了
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
// 转为nanos
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
// 如果此时任务队列中任务数量为0,进入等待逻辑
while (count == 0) {
if (nanos <= 0)
// 等待超时,返回值为null
return null;
// 等待...返回值为剩余的时间
// 如果等待期间内任务队列中添加了新的元素,会被唤醒
nanos = notEmpty.awaitNanos(nanos);
}
// 非空则正常获取任务
return dequeue();
} finally {
lock.unlock();
}
}
假如该工作线程没有获取到新的任务,超时返回NULL了,回到刚刚的主方法
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.
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// Are workers subject to culling?
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
// 此时timed 和 timeOut全部为true,且工作线程数量大于1,条件成立
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
// 自减工作线程计数,操作的是ctl,还没有进入销毁环节
if (compareAndDecrementWorkerCount(c))
// 返回值为null将控制工作线程的实际销毁
return null;
continue;
}
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
// 假设返回的是r = null
if (r != null)
// 正常获取任务时返回任务对象
return r;
//超时标记,没获取任务,进入销毁流程,回到for循环头部
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
// 此时getTask的结果为null,没办法进入while循环了,将进入finally块
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 = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
// 销毁线程
processWorkerExit(w, completedAbruptly);
}
}
3.5、processWorkerExit()——销毁线程
private void processWorkerExit(Worker w, boolean completedAbruptly) {
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
// 被打断的情况下还没有进行ctl自减,需要自减
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 把工作线程中的任务完成数量累加到总完成数量上
completedTaskCount += w.completedTasks;
// 移除工作线程
workers.remove(w);
} finally {
mainLock.unlock();
}
// 尝试终止线程池,如果线程池状态是运行态或者线程池是SHUTDOWN但任务还没处理完是不会终止的
tryTerminate();
// 下面就是控制最小工作线程数量的逻辑
int c = ctl.get();
// 确保当前线程池状态是RUNNING或者SHUTDOWN
if (runStateLessThan(c, STOP)) {
// 确保工作线程不是被打断的
if (!completedAbruptly) {
// allowCoreThreadTimeOut默认为false
// 所以这时工作线程最小值就是核心线程数
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
// 如果参数为true,且任务队列不为空 最小工作线程数就是1了
if (min == 0 && ! workQueue.isEmpty())
min = 1;
// 如果当前线程数量大于核心线程数,不用管
// 如果小于核心线程数,下面会创建新的worker
if (workerCountOf(c) >= min)
return; // replacement not needed
}
// 如果是被打断的,或当前工作线程数量小于最小线程数量
// 新建工作线程
addWorker(null, false);
}
}
经过源码分析,正常情况下如果线程空闲会不分核心非核心线程,会直至减小到corePoolSize数量,但还有另一种情况
4、allowCoreThreadTimeOut(true)——改变最小工作线程数
executor.allowCoreThreadTimeOut(true);
- 默认情况下,
allowCoreThreadTimeOut
为false
,即核心线程即使在空闲时也会保持存活状态,不会被立即回收。 - 当
allowCoreThreadTimeOut
被设置为true
时,核心线程也会使用keepAliveTime
来控制等待工作的超时时间,即当核心线程空闲超过一定时间后,会根据keepAliveTime
来决定是否被回收。 - 如果线程都空闲,决定回收,如果任务队列为空,可能会回收全部线程。如果任务队列不为空,可能会保留一个工作线程