Skip to content

Commit a09a9ca

Browse files
author
haiyang.luo
committed
更新线程池源码分析
1 parent 7cb5f87 commit a09a9ca

5 files changed

Lines changed: 156 additions & 4 deletions

File tree

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package com.learnjava.concurent;
2+
3+
import java.util.concurrent.TimeUnit;
4+
5+
/**
6+
* @Author : haiyang.luo
7+
* @Date : 2026/6/8 00:56
8+
* @Description :
9+
*/
10+
public class InterruptedDemo {
11+
public static void main(String[] args) throws Exception {
12+
// SleepThread 不停地尝试睡眠
13+
Thread sleepThread = new Thread(new SleepRunner(), "SleepThread");
14+
sleepThread.setDaemon(true);
15+
// BusyThread 不停地运行
16+
Thread busyThread = new Thread(new BusyRunner(), "BusyThread");
17+
busyThread.setDaemon(true);
18+
sleepThread.start();
19+
busyThread.start();
20+
// 休眠 5s,让 SleepThread 和 BusyThread 充分运行
21+
TimeUnit.SECONDS.sleep(5);
22+
sleepThread.interrupt();
23+
busyThread.interrupt();
24+
// 休眠 1s,确保主线程能够看到结果
25+
TimeUnit.SECONDS.sleep(1);
26+
System.out.println("SleepThread interrupted is " + sleepThread.isInterrupted());
27+
System.out.println("BusyThread interrupted is " + busyThread.isInterrupted());
28+
// 防止 sleepThread 和 busyThread 立刻退出
29+
TimeUnit.SECONDS.sleep(2);
30+
}
31+
static class SleepRunner implements Runnable {
32+
@Override
33+
public void run() {
34+
while (true) {
35+
try {
36+
TimeUnit.SECONDS.sleep(10);
37+
} catch (InterruptedException e) {
38+
// 捕获中断异常,需要重新标记中断
39+
Thread.currentThread().interrupt();
40+
// throw new RuntimeException(e);
41+
while (true) {
42+
// 让线程保持存活,方便 main 线程观察中断状态
43+
}
44+
}
45+
46+
}
47+
}
48+
}
49+
static class BusyRunner implements Runnable {
50+
@Override
51+
public void run() {
52+
while (true) {
53+
}
54+
}
55+
}
56+
}

note/JDK/深入解析ThreadPoolExecutor底层原理.md

Lines changed: 100 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ CAPACITY: 00011111 11111111 11111111 11111111
7474
总结:掩码就是“过滤器”,CAPACITY 这个掩码专门用来从 ctl 里取出 workerCount。
7575

7676
## 线程池相关状态
77-
线程池完整生命周期图
77+
线程池完整生命周期图(图1)
7878

7979
<!-- 这是一张图片,ocr 内容为: -->
8080
![](https://cdn.nlark.com/yuque/0/2026/png/12890164/1780810463940-3a2fb7db-c019-4d2a-88ed-b395d768d830.png)
@@ -182,6 +182,8 @@ private static final int TERMINATED = 3 << COUNT_BITS;
182182
```
183183

184184
## 二进制图总结
185+
图2
186+
185187
<!-- 这是一张图片,ocr 内容为: -->
186188
![](https://cdn.nlark.com/yuque/0/2026/png/12890164/1780810735409-8b698335-3b31-4cc0-a3cf-d4f57367ac50.png)
187189

@@ -551,8 +553,6 @@ wt.interrupt() 不是杀线程,而是给工作线程设置**<font style="color
551553

552554

553555

554-
555-
556556
> **第二层:为什么要调用 Thread.interrupted()之后,还要再次判断runStateAtLeast(ctl.get(), STOP)**
557557
>
558558
@@ -569,9 +569,11 @@ wt.interrupt() 不是杀线程,而是给工作线程设置**<font style="color
569569

570570

571571

572-
三层判断总体意思就是:只要线程池已经进入 STOP 状态,就要求 worker 必须是中断状态。如果 worker 还不是中断状态,就补一次 interrupt。
572+
三层判断总体意思就是:只要线程池已经进入 STOP 状态,就要求 worker 必须是中断状态。如果 worker 还不是中断状态,就补一次 interrupt。只要线程池已经进入 STOP,就确保当前 worker 线程带着中断标记执行任务。
573+
573574

574575

576+
这一段复杂的判断主要是应对shutdownNow()触发的线程池状态=STOP的场景,而shutdown()是会tryLock()竞争锁,拿不到锁则不会对worker发起中断。
575577

576578
## getTask()
577579
getTask()的作用:是 ThreadPoolExecutor 里 Worker 从阻塞队列获取任务的核心方法。
@@ -780,6 +782,29 @@ public List<Runnable> shutdownNow() {
780782

781783

782784

785+
shutdownNow()会立马让线程池状态推进到STOP,然后调用interruptWorkers()中断所有worker,因此worker在runWorker()中拿到锁准备执行用户任务task.run()之前,也要先进行判断:
786+
787+
```java
788+
if ((runStateAtLeast(ctl.get(), STOP) ||
789+
(Thread.interrupted() &&
790+
runStateAtLeast(ctl.get(), STOP))) &&
791+
!wt.isInterrupted())
792+
// 只要线程池已经进入 STOP,就确保当前 worker 线程带着中断标记执行任务。
793+
wt.interrupt();
794+
```
795+
796+
wt.interrupt()最后还是会给执行用户任务的线程标记“中断”,最后中断是否会导致worker线程“结束”任务,具体看用户线程是否调用了能响应中断的逻辑,比如:
797+
798+
```java
799+
Thread.sleep()
800+
BlockingQueue.take()
801+
Condition.await()
802+
```
803+
804+
这类方法会抛出InterruptedException提前结束。
805+
806+
或者任务代码自己判断 Thread.currentThread().isInterrupted(),然后主动退出。如果用户任务完全不理中断,比如死循环不检查中断,那么 shutdownNow()也不能强制让它停下来。
807+
783808
### shutdown和shutdownNow的区别
784809
> **<font style="color:#DF2A3F;">shutdown</font>**
785810
>
@@ -832,3 +857,74 @@ try {
832857

833858

834859

860+
861+
862+
863+
864+
## 线程池任务调用流程图
865+
图3
866+
867+
<!-- 这是一张图片,ocr 内容为: -->
868+
![](https://cdn.nlark.com/yuque/0/2026/png/12890164/1780886199772-60d2b78f-8991-4c11-afe9-8211912af596.png)
869+
870+
# 其他细节
871+
## execute()入队之后的二次检查
872+
execute() 入队后的二次检查,不只是防止任务丢失
873+
874+
```java
875+
if (isRunning(c) && workQueue.offer(command)) {
876+
int recheck = ctl.get();
877+
if (!isRunning(recheck) && remove(command))
878+
reject(command);
879+
else if (workerCountOf(recheck) == 0)
880+
addWorker(null, false);
881+
}
882+
```
883+
884+
二次检查作用:防止线程池关闭后任务被错误保留,以及队列有任务但没有 worker 执行。
885+
886+
887+
888+
## Worker本身就是一个锁(AQS)
889+
源码中可以看到,Worker继承了AbstractQueuedSynchronizer
890+
891+
```java
892+
Worker extends AbstractQueuedSynchronizer
893+
```
894+
895+
因此在调用runWorker()时会先加锁,然后执行用户任务,等结束了再释放锁:
896+
897+
```java
898+
...
899+
w.lock();
900+
...
901+
task.run();
902+
...
903+
w.unlock();
904+
```
905+
906+
这段代码核心作用:标记当前 worker 正在执行任务,防止 shutdown() 中断它。但是没法防止shutdownNow()进行中断,中断与否则是要看用户任务,在上文shutdownNow()有详细分析。
907+
908+
## 核心线程和非核心线程没有身份标记
909+
线程池里没有字段标记:
910+
911+
```plain
912+
这个 worker 是核心线程
913+
那个 worker 是非核心线程
914+
```
915+
916+
而是动态判断:
917+
918+
```plain
919+
workerCount > corePoolSize
920+
```
921+
922+
超过 `corePoolSize` 的线程,空闲时就可能被回收。
923+
924+
## 拒绝策略不只在队列满时触发
925+
常见理解是:队列满 + 线程数满 -> reject
926+
927+
但其实还有:线程池不是 RUNNING 状态,也会 reject。
928+
929+
例如已经shutdown()后再提交任务,则会被reject。
930+
82.4 KB
Loading
124 KB
Loading
1.38 MB
Loading

0 commit comments

Comments
 (0)