Java 線程生命周期與狀態(tài)轉(zhuǎn)換的核心路徑
一、前言
在上一篇文章中,我們學(xué)習(xí)了 Java 中創(chuàng)建線程的 5 種方式,掌握了線程的基礎(chǔ)創(chuàng)建與啟動(dòng)邏輯。但線程從創(chuàng)建到銷毀并非一成不變,而是會(huì)在不同狀態(tài)之間轉(zhuǎn)換。理解線程的生命周期和狀態(tài)轉(zhuǎn)換,是解決多線程并發(fā)問(wèn)題的關(guān)鍵。
本文將從理論到實(shí)踐,深入解析 Java 線程的 6 種狀態(tài),演示狀態(tài)轉(zhuǎn)換的核心路徑,并通過(guò)工具分析線程狀態(tài),幫助大家徹底掌握這一核心知識(shí)點(diǎn)。
二、Java 線程的 6 種狀態(tài)
Java 線程的狀態(tài)被明確枚舉在 Thread.State 中,共包含 6 種狀態(tài) ,任何時(shí)刻一個(gè)線程只能處于其中一種狀態(tài)。
狀態(tài)名稱 | 核心含義 | 進(jìn)入條件 | 退出條件 |
|---|---|---|---|
NEW (新建) | 線程對(duì)象已創(chuàng)建,但未調(diào)用start()方法 | 創(chuàng)建 Thread 實(shí)例后,未執(zhí)行 start() | 調(diào)用 start() 方法 |
RUNNABLE (可運(yùn)行) | 線程正在 CPU 上執(zhí)行,或處于就緒狀態(tài)等待 CPU 調(diào)度 | 調(diào)用 start() 方法;從其他阻塞 / 等待狀態(tài)喚醒 | 線程執(zhí)行完畢;進(jìn)入阻塞 / 等待狀態(tài) |
BLOCKED (阻塞) | 線程因競(jìng)爭(zhēng)鎖失敗而被阻塞 | 線程嘗試進(jìn)入 synchronized 代碼塊 / 方法,但鎖被其他線程持有 | 成功獲取到鎖 |
WAITING (等待) | 線程無(wú)時(shí)間限制等待,需其他線程主動(dòng)喚醒 | 調(diào)用 Object.wait()(無(wú)參)、Thread.join()(無(wú)參)、LockSupport.park() | 其他線程調(diào)用 Object.notify()/notifyAll()、被 join 的線程執(zhí)行完畢、調(diào)用 LockSupport.unpark() |
TIMED_WAITING (超時(shí)等待) | 線程有時(shí)間限制等待,超時(shí)后自動(dòng)喚醒 | 調(diào)用 Thread.sleep(long)、Object.wait(long)、Thread.join(long)、LockSupport.parkNanos()/parkUntil() | 超時(shí)自動(dòng)喚醒;其他線程提前喚醒 |
TERMINATED (終止) | 線程執(zhí)行完畢或異常退出 | run() 方法執(zhí)行完成;線程執(zhí)行過(guò)程中拋出未捕獲異常 | 無(wú)(線程生命周期結(jié)束) |
三、線程狀態(tài)轉(zhuǎn)換流程圖

四、線程狀態(tài)轉(zhuǎn)換
線程狀態(tài)轉(zhuǎn)換有固定的核心路徑,我們通過(guò)代碼案例逐一演示關(guān)鍵轉(zhuǎn)換場(chǎng)景,直觀觀察狀態(tài)變化。
前置說(shuō)明
- 所有案例中,使用 Thread.getState() 方法獲取線程當(dāng)前狀態(tài)。
- 為了精準(zhǔn)觀察狀態(tài),使用 Thread.sleep() 控制線程執(zhí)行節(jié)奏,避免因 CPU 調(diào)度過(guò)快導(dǎo)致狀態(tài)無(wú)法捕捉。
場(chǎng)景 1:NEW → RUNNABLE → TERMINATED(基礎(chǔ)路徑)
核心邏輯 :線程創(chuàng)建后處于 NEW 狀態(tài),調(diào)用 start() 進(jìn)入 RUNNABLE ,執(zhí)行完 run() 后進(jìn)入 TERMINATED 。
public class ThreadStateDemo1 {
public static void main(String[] args) throws InterruptedException {
// 1. 創(chuàng)建線程,此時(shí)狀態(tài)為 NEW
Thread thread = new Thread(() -> {
System.out.println("線程執(zhí)行中,當(dāng)前狀態(tài):" + Thread.currentThread().getState());
// 模擬任務(wù)執(zhí)行
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "基礎(chǔ)狀態(tài)線程");
System.out.println("調(diào)用start()前,線程狀態(tài):" + thread.getState()); // NEW
// 2. 啟動(dòng)線程,進(jìn)入 RUNNABLE 狀態(tài)
thread.start();
Thread.sleep(50); // 等待線程進(jìn)入執(zhí)行狀態(tài)
System.out.println("線程執(zhí)行中,主線程觀察到的狀態(tài):" + thread.getState()); // RUNNABLE
// 3. 等待線程執(zhí)行完畢
thread.join();
System.out.println("線程執(zhí)行完畢,當(dāng)前狀態(tài):" + thread.getState()); // TERMINATED
}
}運(yùn)行結(jié)果
調(diào)用start()前,線程狀態(tài):NEW
線程執(zhí)行中,當(dāng)前狀態(tài):RUNNABLE
線程執(zhí)行中,主線程觀察到的狀態(tài):RUNNABLE
線程執(zhí)行完畢,當(dāng)前狀態(tài):TERMINATED
場(chǎng)景 2:RUNNABLE → BLOCKED → RUNNABLE(鎖競(jìng)爭(zhēng)導(dǎo)致阻塞)
核心邏輯 :線程嘗試獲取 synchronized 鎖失敗時(shí),進(jìn)入 BLOCKED 狀態(tài);成功獲取鎖后,回到 RUNNABLE 。
public class ThreadStateDemo2 {
// 定義一個(gè)鎖對(duì)象
private static final Object LOCK = new Object();
public static void main(String[] args) throws InterruptedException {
// 線程1:先獲取鎖,持有鎖1秒
Thread thread1 = new Thread(() -> {
synchronized (LOCK) {
try {
System.out.println(Thread.currentThread().getName() + " 獲取到鎖,狀態(tài):RUNNABLE");
Thread.sleep(1000); // 持有鎖,讓線程2阻塞
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "線程1");
// 線程2:后獲取鎖,會(huì)進(jìn)入 BLOCKED 狀態(tài)
Thread thread2 = new Thread(() -> {
synchronized (LOCK) {
System.out.println(Thread.currentThread().getName() + " 成功獲取鎖,狀態(tài)回到 RUNNABLE");
}
}, "線程2");
// 啟動(dòng)線程1和線程2
thread1.start();
Thread.sleep(100); // 確保線程1先獲取鎖
thread2.start();
// 觀察線程2的狀態(tài)
Thread.sleep(200);
System.out.println(thread2.getName() + " 等待鎖,狀態(tài):" + thread2.getState()); // BLOCKED
// 等待線程1執(zhí)行完畢,線程2獲取鎖
thread1.join();
Thread.sleep(200);
System.out.println(thread2.getName() + " 執(zhí)行中,狀態(tài):" + thread2.getState()); // RUNNABLE
}
}運(yùn)行結(jié)果
線程1 獲取到鎖,狀態(tài):RUNNABLE
線程2 等待鎖,狀態(tài):BLOCKED
線程2 成功獲取鎖,狀態(tài)回到 RUNNABLE
線程2 執(zhí)行中,狀態(tài):RUNNABLE
場(chǎng)景 3:RUNNABLE → WAITING → RUNNABLE(無(wú)參 wait () 喚醒)
核心邏輯 :線程調(diào)用 Object.wait() (無(wú)參)后,釋放鎖并進(jìn)入 WAITING 狀態(tài);需其他線程調(diào)用 notify() / notifyAll() 喚醒,重新競(jìng)爭(zhēng)鎖后回到 RUNNABLE 。
public class ThreadStateDemo3 {
private static final Object LOCK = new Object();
private static Thread waitThread; // 等待線程
public static void main(String[] args) throws InterruptedException {
// 等待線程:調(diào)用wait()進(jìn)入WAITING狀態(tài)
waitThread = new Thread(() -> {
synchronized (LOCK) {
try {
System.out.println(Thread.currentThread().getName() + " 調(diào)用wait(),進(jìn)入WAITING狀態(tài)");
LOCK.wait(); // 無(wú)參wait,釋放鎖
System.out.println(Thread.currentThread().getName() + " 被喚醒,狀態(tài)回到RUNNABLE");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "等待線程");
// 喚醒線程:調(diào)用notify()喚醒等待線程
Thread notifyThread = new Thread(() -> {
synchronized (LOCK) {
System.out.println(Thread.currentThread().getName() + " 獲取鎖,準(zhǔn)備喚醒等待線程");
LOCK.notify(); // 喚醒等待線程
try {
Thread.sleep(500); // 持有鎖,讓等待線程先處于WAITING→BLOCKED
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "喚醒線程");
waitThread.start();
Thread.sleep(200);
System.out.println(waitThread.getName() + " 當(dāng)前狀態(tài):" + waitThread.getState()); // WAITING
notifyThread.start();
notifyThread.join();
Thread.sleep(200);
System.out.println(waitThread.getName() + " 最終狀態(tài):" + waitThread.getState()); // RUNNABLE/TERMINATED
}
}運(yùn)行結(jié)果
等待線程 調(diào)用wait(),進(jìn)入WAITING狀態(tài)
等待線程 當(dāng)前狀態(tài):WAITING
喚醒線程 獲取鎖,準(zhǔn)備喚醒等待線程
等待線程 被喚醒,狀態(tài)回到RUNNABLE
等待線程 最終狀態(tài):TERMINATED
場(chǎng)景 4:RUNNABLE → TIMED_WAITING → RUNNABLE(sleep () 超時(shí)喚醒)
核心邏輯 :線程調(diào)用 Thread.sleep(long) 后,進(jìn)入 TIMED_WAITING 狀態(tài);超時(shí)后自動(dòng)喚醒,回到 RUNNABLE 狀態(tài)( sleep () 不會(huì)釋放鎖 )。
public class ThreadStateDemo4 {
public static void main(String[] args) throws InterruptedException {
Thread sleepThread = new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + " 調(diào)用sleep(1000),進(jìn)入TIMED_WAITING狀態(tài)");
Thread.sleep(1000); // 超時(shí)等待1秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 超時(shí)喚醒,狀態(tài)回到RUNNABLE");
}, "睡眠線程");
sleepThread.start();
Thread.sleep(200);
System.out.println(sleepThread.getName() + " 當(dāng)前狀態(tài):" + sleepThread.getState()); // TIMED_WAITING
sleepThread.join();
System.out.println(sleepThread.getName() + " 執(zhí)行完畢,狀態(tài):" + sleepThread.getState()); // TERMINATED
}
}運(yùn)行結(jié)果
睡眠線程 調(diào)用sleep(1000),進(jìn)入TIMED_WAITING狀態(tài)
睡眠線程 當(dāng)前狀態(tài):TIMED_WAITING
睡眠線程 超時(shí)喚醒,狀態(tài)回到RUNNABLE
睡眠線程 執(zhí)行完畢,狀態(tài):TERMINATED
五、使用 jstack 命令分析線程狀態(tài)
在實(shí)際開發(fā)中,我們無(wú)法通過(guò)代碼實(shí)時(shí)觀察線上線程的狀態(tài),此時(shí)可以使用 JDK 自帶的 jstack 命令,查看線程的狀態(tài)和調(diào)用棧,定位線程阻塞、死鎖等問(wèn)題。
1、操作步驟
1. 獲取進(jìn)程 ID:使用 jps 命令,查看 Java 進(jìn)程的 PID。
jps # 輸出示例:1234 ThreadStateDemo2 (1234為PID)
2. 導(dǎo)出線程快照:使用 jstack PID > thread_dump.txt 命令,將線程快照導(dǎo)出到文件。
jstack 1234 > thread_dump.txt
3. 分析線程狀態(tài):打開 thread_dump.txt 文件,搜索線程名稱,查看線程狀態(tài)。
2、實(shí)戰(zhàn)案例(分析 BLOCKED 狀態(tài)線程)
在 ThreadStateDemo2 運(yùn)行時(shí),導(dǎo)出線程快照,會(huì)看到類似如下內(nèi)容:
"線程2" #13 prio=5 os_prio=31 tid=0x00007f8b1282e000 nid=0x5003 waiting for monitor entry [0x0000700001a6f000]
java.lang.Thread.State: BLOCKED (on object monitor)
at ThreadStateDemo2.lambda$main$1(ThreadStateDemo2.java:18)
- waiting to lock <0x000000076ab81810> (a java.lang.Object)
- locked <0x000000076ab81810> (a java.lang.Object)- java.lang.Thread.State: BLOCKED (on object monitor):明確線程處于 BLOCKED 狀態(tài)。
- waiting to lock <0x000000076ab81810>:表示線程正在等待獲取該地址的鎖。
六、常見(jiàn)誤區(qū)辨析
- RUNNABLE 狀態(tài) ≠ 正在執(zhí)行:RUNNABLE 包含 “就緒” 和 “運(yùn)行中” 兩種情況,只有獲取到 CPU 時(shí)間片的線程才會(huì)真正執(zhí)行。
- sleep () 和 wait () 的區(qū)別: sleep() 屬于 Thread 類,不會(huì)釋放鎖,超時(shí)自動(dòng)喚醒; wait() 屬于 Object 類,會(huì)釋放鎖,需要其他線程喚醒。
- BLOCKED 和 WAITING 的區(qū)別: BLOCKED 是因競(jìng)爭(zhēng)鎖阻塞, WAITING 是因調(diào)用 wait() / join() 等方法主動(dòng)等待。
六、總結(jié)
本文詳細(xì)講解了 Java 線程的 6 種狀態(tài),通過(guò) 4 個(gè)代碼案例演示了狀態(tài)轉(zhuǎn)換的核心路徑,并介紹了使用 jstack 命令分析線程狀態(tài)的實(shí)戰(zhàn)方法。理解線程的生命周期和狀態(tài)轉(zhuǎn)換,是后續(xù)學(xué)習(xí)線程同步、線程通信的基礎(chǔ)。
下一篇文章,我們將深入分析并發(fā)編程的三大核心特性(原子性、可見(jiàn)性、有序性),并講解volatile關(guān)鍵字的原理與使用場(chǎng)景。
到此這篇關(guān)于Java 線程生命周期與狀態(tài)轉(zhuǎn)換的文章就介紹到這了,更多相關(guān)java線程生命周期內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java線程生命周期的終止與復(fù)位
- Java線程生命周期及轉(zhuǎn)換過(guò)程
- 深入探究Java線程的狀態(tài)與生命周期
- Java線程的生命周期的詳解
- java 線程的生命周期詳解
- Java線程狀態(tài)轉(zhuǎn)換的詳細(xì)過(guò)程
- Java實(shí)現(xiàn)枚舉狀態(tài)轉(zhuǎn)換的方法詳解
- Java狀態(tài)設(shè)計(jì)模式實(shí)現(xiàn)對(duì)象狀態(tài)轉(zhuǎn)換的優(yōu)雅方式
- java?線程池狀態(tài)及狀態(tài)轉(zhuǎn)換
- Java線程狀態(tài)轉(zhuǎn)換關(guān)系實(shí)例解析
相關(guān)文章
Java中實(shí)現(xiàn)代碼優(yōu)化的技巧分享
這篇文章主要跟大家談?wù)剝?yōu)化這個(gè)話題,那么我們一起聊聊Java中如何實(shí)現(xiàn)代碼優(yōu)化這個(gè)問(wèn)題,小編這里有幾個(gè)實(shí)用的小技巧分享給大家,需要的可以參考一下2022-08-08
mybatisplus where QueryWrapper加括號(hào)嵌套查詢方式
這篇文章主要介紹了mybatisplus where QueryWrapper加括號(hào)嵌套查詢方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教。2022-01-01
Java使用ByteBuffer進(jìn)行多文件合并和拆分的代碼實(shí)現(xiàn)
因?yàn)轵?yàn)證證書的需要,需要把證書文件和公鑰給到客戶,考慮到多個(gè)文件交互的不便性,所以決定將2個(gè)文件合并成一個(gè)文件交互給客戶,但是由于是加密文件,采用字符串形式合并后,拆分后文件不可用,本文給大家介紹了Java使用ByteBuffer進(jìn)行多文件合并和拆分,需要的朋友可以參考下2024-09-09
Java通過(guò)在主循環(huán)中判斷Boolean來(lái)停止線程的方法示例
這篇文章主要介紹了Java通過(guò)在主循環(huán)中判斷Boolean來(lái)停止線程的方法,結(jié)合具體實(shí)例形式分析了java針對(duì)線程的判斷與停止操作相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2017-04-04
PowerJob的ProcessorLoader工作流程源碼解讀
這篇文章主要為大家介紹了PowerJob的ProcessorLoader工作流程源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12
JAVA中try-catch結(jié)構(gòu)之異常處理的使用方法
Java編程中一個(gè)非常重要且實(shí)用的概念,可以幫助我們處理代碼運(yùn)行時(shí)發(fā)生的異常情況,下面這篇文章主要給大家介紹了關(guān)于JAVA中try-catch結(jié)構(gòu)之異常處理的使用方法,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-09-09
詳解Mybatis多參數(shù)傳遞入?yún)⑺姆N處理方式
這篇文章主要介紹了詳解Mybatis多參數(shù)傳遞入?yún)⑺姆N處理方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04
Java?map為什么不能遍歷的同時(shí)進(jìn)行增刪操作
這篇文章主要介紹了Java?map為什么不能遍歷的同時(shí)進(jìn)行增刪操作,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-07-07

