一、线程池的用法
简单用法,这里写一个简单的demo
java
// 线程池
public static void executePool() throws ExecutionException, InterruptedException {
// 手动创建线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
4,
4,
100, // 非核心线程空闲时间
TimeUnit.MILLISECONDS, // 超时单位
new LinkedBlockingQueue<>(1000),
new ThreadFactory() { // 线程工厂
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
return thread;
}
},
new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);
// 执行任务,没有返回值的任务
for (int i = 0; i < 5; i++) {
executor.execute(() -> {
System.out.println("没有返回值!!!" + Thread.currentThread());
});
}
// 执行任务,有返回值的任务
String result = executor.submit(() -> {
return "100";
}).get();
System.out.println(result);
}
线程池线程大小的设置可以从两方面来说
CPU密集、IO 密集
CPU 密集:核心线程数:CPU 核心数 + 1 【CPU 一直在执行指令,进行大量计算】
IO 密集:核心线程数:CPU 内核数 * 2 【CPU 一直在做 IO 调用】
一般业务上,不会按这个走,业务上有不同的任务,不同的任务用不同的线程池,可能一个服务里面有多个线程池
二、线程池的核心属性
java
// AtomicInteger ,就是一个int,用 CAS 来保证原子性
// 高三位表示线程池状态
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// 29
private static final int COUNT_BITS = Integer.SIZE - 3;
// 工作线程最大个数
private static final int COUNT_MASK = (1 << COUNT_BITS) - 1;
// 线程池的几个状态
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 & ~COUNT_MASK; }
// 获取工作线程个数
private static int workerCountOf(int c) { return c & COUNT_MASK; }
三、线程池状态流转

四、excute方法
线程池处理任务的流程,当新任务来了是先放队列还是开辟新的线程处理,都在这个逻辑里
scss
public void execute(Runnable command) {
// 执行的任务为空,抛异常
if (command == null){
throw new NullPointerException();
}
// 拿到 ctl
int c = ctl.get();
// 通过c获取工作线程数 判断 是否小于 核心线程数
if (workerCountOf(c) < corePoolSize) {
// addWorker() 第二个参数 含义:true : 核心线程 false : 代表非核心线程
if (addWorker(command, true)){
// 添加核心线程执行任务成功 return
return;
}
// 添加失败 重新获取 ctl 的值
c = ctl.get();
}
// 判断线程池状态 < Shutdown
// 线程状态为 Running状态的同时,将任务添加进队列
if (isRunning(c) && workQueue.offer(command)) {
// doublecheck 再次获取 ctl值
int recheck = ctl.get();
// 线程池状态非 Running状态 从工作队列移除掉任务
if (!isRunning(recheck) && remove(command)){
// 走一波拒绝策略
reject(command);
}else if (workerCountOf(recheck) == 0){ // 线程池状态是 Running -> 线程池的工作线程数为 0 进这个if分支
// 进这个if还有一个说法:可以将核心线程数设置为0,所有的工作线程都是非核心线程
// 情况二:核心线程数可以通过keepAlive 来销毁,如果此时核心线程刚好被销毁,工作线程可能为 0
// 添加一个非核心线程的空任务 去处理工作队列中的任务
addWorker(null, false);
}
}else if (!addWorker(command, false)){ // 启动非核心线程去处理工作队列里的任务失败 ---> 线程满了
// 根据上下条件判断:什么时候进行这个if的条件判断 要么线程池状态不是 Running , 要么 任务添加到队列失败
// 走一波拒绝策略
reject(command);
}
}
五、addWorker添加工作线程并处理任务
从这个方法就可以看出,一个线程就是一个 Worker
这个方法分为两大部分:
1、CAS 自增 ctl 工作线程数
2、步骤一成功:new 一个 Worker 处理任务
java
/**
* firstTask : 任务
* core : 是否是核心线程
*/
private boolean addWorker(Runnable firstTask, boolean core) {
// 下面这个两层的for循环 --> 给ctl线程数 + 1
retry:
for (int c = ctl.get();;) { // 外层循环 --> 校验线程池状态
// 边界校验 线程池状态不是runing 且 [线程池状态至少是 STOP 或者 传进来的任务不是null 或者 任务队列是空]
if (runStateAtLeast(c, SHUTDOWN)
&& (runStateAtLeast(c, STOP)
|| firstTask != null
|| workQueue.isEmpty())){
// 不满足创建新线程的条件 ,直接return
return false;
}
for (;;) { // 内层循环:尝试增加工作线程数
// 工作线程数是否超过阈值(核心线程数 / 最大线程数)
if (workerCountOf(c)
>= ((core ? corePoolSize : maximumPoolSize) & COUNT_MASK)){
// 线程数已达上限,不能再新增
return false;
}
// CAS 工作线程数自增
if (compareAndIncrementWorkerCount(c))
// 跳出外层循环
break retry;
// CAS 失败,重新获取ctl
c = ctl.get();
if (runStateAtLeast(c, SHUTDOWN)) // 线程池状态至少是 ShutDown
continue retry; // 继续下一次外层循环,重新进行校验
// 否则 重试 CAS 自增线程数
}
}
// 这一部分 :启动线程处理任务
boolean workerStarted = false; // 工作线程是否启动
boolean workerAdded = false; // Worker是否成功加入线程池的管理集合
Worker w = null; // 工作线程默认为null
try {
// new 一个工作线程出来
w = new Worker(firstTask);
// 拿到thread
final Thread t = w.thread;
if (t != null) { // 除非自己声明的线程工厂返回的是 null ,否则一定不为 null
// 获取线程池的全局锁
final ReentrantLock mainLock = this.mainLock;
// 之所以要获取锁资源:因为在启动这个线程时,避免线程池状态发生变化 --> 线程池调用shutDown方法时也会获取这个锁
mainLock.lock();
try {
// 重新获取ctl
int c = ctl.get();
// 判断线程池状态
// 场景1:线程池处于RUNNING状态 → 无条件允许
// 场景2:线程池状态<STOP(即SHUTDOWN)且无初始任务 → 允许创建Worker处理队列残留任务
if (isRunning(c) || (runStateLessThan(c, STOP) && firstTask == null)) {
// 如果thread不是 new 已经被启动 抛异常
if (t.getState() != Thread.State.NEW){
throw new IllegalThreadStateException();
}
// 将工作线程添加到HashSet中 统一管理
workers.add(w);
// 工作线程添加成功
workerAdded = true;
// 获取工作线程个数,判断是否需要修改最大工作线程数记录【监控用】
int s = workers.size();
if (s > largestPoolSize){
largestPoolSize = s;
}
}
} finally {
// finally 里解锁
mainLock.unlock();
}
if (workerAdded) { // 判断工作线程是否添加成功
// 启动线程 实际是调用t.start 来启动线程
container.start(t);
// 线程启动成功
workerStarted = true;
}
}
} finally {
// 判断工作线程是否启动成功
if (! workerStarted){
// 启动失败,走这 新增工作线程失败
addWorkerFailed(w);
}
}
return workerStarted;
}
// Worker 继承了 AQS ,构造器如下
Worker(Runnable firstTask) {
setState(-1);
this.firstTask = firstTask;
// 线程工厂获取新线程
this.thread = getThreadFactory().newThread(this);
}
shutDown的时候也会获取全局锁

添加工作线程时,之所以要加锁,就是为了防止并发时,线程池的状态被修改
六、runWorker方法
在addWorker时,在最终启动线程时,会调用t.start()方法,会执行 Worker里的run()方法,因为 Worker实现了 Runnable接口,重写了run方法,最终会走到worker里的run方法。

csharp
public void run() {
// 调用runWorker
runWorker(this);
}
scss
// worker传进来的是this, 当前对象
final void runWorker(Worker w) {
// 拿到当前线程
Thread wt = Thread.currentThread();
// 拿到当前携带的第一个任务
Runnable task = w.firstTask;
// 将worker中的任务清空,避免重复执行
w.firstTask = null;
w.unlock(); // 解锁
// 是否异常完成,默认为true 表示 任务执行异常
boolean completedAbruptly = true;
try {
// 这是一个循环,task不为空 或者 能从队列里拿到任务,就一直执行
while (task != null || (task = getTask()) != null) {
// 加锁开始执行 --> 防止线程池停止时中断执行的任务
w.lock();
// 再来一个校验 : 线程池状态至少是 Stop ,但是当前线程还没被中断 就得执行中断
// 线程池 STop 状态,停止执行任何任务
if ((runStateAtLeast(ctl.get(), STOP) || (Thread.interrupted() && runStateAtLeast(ctl.get(), STOP))) && !wt.isInterrupted())
// 当前线程要中断
wt.interrupt();
try {
// 前置钩子 --> 让自己实现
beforeExecute(wt, task);
try {
// 执行任务
task.run();
// 后置钩子 --> 让自己实现
afterExecute(task, null);
} catch (Throwable ex) {
afterExecute(task, ex);
throw ex;
}
} finally {
task = null;
// 当前任务完成,就++ 统计完成了多少个任务
w.completedTasks++;
// 解锁
w.unlock();
}
}
// 是否出现异常完成 --> false 正常完成
completedAbruptly = false;
} finally {
// 执行线程退出
processWorkerExit(w, completedAbruptly);
}
}
线程池线程复用的关键也在这块:worker启动之后,会一直从队列里取任务,直到没有任务可取才会被销毁。
七、getTask()取任务
再接着分析getTask从工作队列里取任务是怎么取的
java
private Runnable getTask() {
// 是否超时
boolean timedOut = false;
for (;;) {
// 获取ctl值
int c = ctl.get();
// 线程池状态至少为 ShutDown 并且队列已经空了
if (runStateAtLeast(c, SHUTDOWN)
&& (runStateAtLeast(c, STOP) || workQueue.isEmpty())) {
// 对工作线程个数减一
decrementWorkerCount();
return null;
}
// 获取工作线程个数
int wc = workerCountOf(c);
// 可超时回收标记:如果工作线程个数 大于 核心线程数 允许超时 --> 说明是非核心线程
// allowCoreThreadTimeOut --> 默认false 允许核心线程数超时退出
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
// 条件一:工作线程数 大于 最大线程数 或者 非核心线程超时了
// 条件二:工作线程数大于 1 但是 队列空了
// 两个条件同时满足
if ((wc > maximumPoolSize || (timed && timedOut)) && (wc > 1 || workQueue.isEmpty())) {
// 对工作线程数减一 直接return
if (compareAndDecrementWorkerCount(c))
return null;
// CAS 失败,continue自旋
continue;
}
try {
// 根据time来判断是否可超时回收
// 如果可以超时回收 ,就自己从队列里拉,等待keepAliveTime后仍无任务返回null
// 不可超时回收,调用take,阻塞等待直到拿到任务(核心线程默认行为)
Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take();
if (r != null){
// 从队列里拿到任务 返回
return r;
}
timedOut = true; // 超时没有拿到,标记为true 下次循环触发退出校验
} catch (InterruptedException retry) {
// 捕获中断异常,中断不算超时,重置标记,重新循环尝试获取任务
timedOut = false;
}
}
}
这个方法整体分为三大块:
一:线程池状态判断 和 任务队列是否为空判断 --> 提前校验
二:判断是否可以超时回收:获取工作线程个数,判断是否是核心线程,核心线程不可超时回收,非核心线程可以超时回收
三:根据是否可以超时回收:非核心线程用poll拉 标记keepAliveTime ,到了时间还没拿到任务返回null。核心线程用take,阻塞等待,直到拿到任务
八、processWorkerExit执行线程退出
scss
// w --> worker工作线程
// completedAbruptly --> false正常完成任务 true非正常完成任务
private void processWorkerExit(Worker w, boolean completedAbruptly) {
// 判断是否正常完成任务
if (completedAbruptly){
// 非正常完成任务 工作线程数减一
decrementWorkerCount();
}
// 获取全局锁
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 统计累计完成的任务数
completedTaskCount += w.completedTasks;
// 从hashSet中移除掉worker
workers.remove(w);
} finally {
mainLock.unlock();
}
// 尝试终止线程池,线程池状态是否发生变化
tryTerminate();
int c = ctl.get();
// 线程池的状态小于 Stop
if (runStateLessThan(c, STOP)) {
// 任务正常完成
if (!completedAbruptly) {
// 判断 队列是否为空,如果队列不为空,判断工作线程是否大于等于 1 如果还有工作线程直接return
int min = allowCoreThreadTimeOut ? 0 : corePoolSize; // 如果核心线程可以超时,那么整个线程池就没有线程可用
if (min == 0 && ! workQueue.isEmpty())
min = 1;
if (workerCountOf(c) >= min) // 如果还有核心线程在阻塞,直接return
return;
}
// 如果工作线程为空
// 添加一个非核心空任务的线程来处理队列里的任务
addWorker(null, false);
}
}
线程退出这里分为两大部分:
部分一:从维护的全局 HashSet里移除掉worker + 尝试终止线程池
部分二:线程池的状态如果是 Running/ShutDown,判断任务队列是否为空,不为空,核心线程在阻塞着,return,用核心线程处理,工作线程数为空,添加一个空任务的非核心线程去处理队列里的任务
九、拒绝策略

就这四个拒绝策略
9.1、AbortPolicy
抛出异常
java
public static class AbortPolicy implements RejectedExecutionHandler {
/**
* Creates an {@code AbortPolicy}.
*/
public AbortPolicy() { }
/**
* Always throws RejectedExecutionException.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
* @throws RejectedExecutionException always
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
9.2、DiscardPolicy
什么也不做,也不抛出异常
java
public static class DiscardPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardPolicy}.
*/
public DiscardPolicy() { }
/**
* Does nothing, which has the effect of discarding task r.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
9.3、DiscardOldestPolicy
从队列里边扔出一个,把当前任务给执行掉
java
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardOldestPolicy} for the given executor.
*/
public DiscardOldestPolicy() { }
/**
* Obtains and ignores the next task that the executor
* would otherwise execute, if one is immediately available,
* and then retries execution of task r, unless the executor
* is shut down, in which case task r is instead discarded.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}
9.4、CallerRunsPolicy
谁提交谁执行
java
public static class CallerRunsPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code CallerRunsPolicy}.
*/
public CallerRunsPolicy() { }
/**
* Executes task r in the caller's thread, unless the executor
* has been shut down, in which case the task is discarded.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
十、总结
线程池的源码看完还是很混乱,里面有太多的线程池状态判断以及 Double Check,影响主思路的梳理,现在写一下核心流程:
