一、線程生命週期:七大狀態與轉換圖解
Java 線程從創建到消亡,經歷以下狀態(Thread.State 枚舉):
| 狀態 | 枚舉常量 | 觸發條件/含義 |
| 新建 | NEW | new Thread(),未調用 start(),僅分配記憶體結構 |
| 就緒 | RUNNABLE | 調用 start() 後,等待 CPU 調度(含運行中) |
| 運行 | RUNNABLE | 獲得 CPU 時間片,執行 run() 方法體 |
| 阻塞 | BLOCKED | 等待獲取 synchronized 鎖(進入同步塊/方法) |
| 等待 | WAITING | 無限期等待:Object.wait()、Thread.join()、LockSupport.park() |
| 計時等待 | TIMED_WAITING | 帶超時等待:Thread.sleep()、Object.wait(timeout)、join(timeout)、LockSupport.parkNanos/Until() |
| 終止 | TERMINATED | run() 正常返回、拋出未捕獲異常、調用 stop()(已棄用) |
狀態轉換關鍵路徑
NEW --start()--> RUNNABLE (就緒/運行)
RUNNABLE --sleep()/wait(timeout)/join(timeout)--> TIMED_WAITING --超時/中斷--> RUNNABLE
RUNNABLE --wait()/join()/park()--> WAITING --notify()/notifyAll()/interrupt()/unpark()--> RUNNABLE
RUNNABLE --synchronized 鎖競爭失敗--> BLOCKED --獲得鎖--> RUNNABLE
RUNNABLE --run() 結束/異常--> TERMINATED
二、上下文切換:多線程效能的隱形代價
- 定義:CPU 從執行線程 A 切換到線程 B,需保存 A 的程序計數器(PC)、CPU 寄存器狀態、線程私有棧指針等上下文,恢復 B 的上下文。
- 觸發場景:時間片耗盡、主動讓出(
sleep/yield/wait/join)、鎖競爭阻塞、I/O 阻塞、中斷、高優先級線程搶佔。
- 代價:微秒級延遲、緩存未命中(Cache Miss)、系統調用開銷。高併發下頻繁切換會嚴重拖垮吞吐量。
- 優化方向:減少鎖粒度、使用無鎖數據結構、合理設定線程池大小(避免過多線程)、採用異步非阻塞 I/O(Netty/Reactor)。
三、Thread 核心方法深度解析
| 方法 | 作用 | 關鍵行為與注意點 |
start() | 啟動線程 | 異步啟動,JVM 分配資源、創建調用棧、將線程置為 RUNNABLE。**不可重複調用**(會拋 IllegalThreadStateException)。 |
run() | 執行邏輯 | **不直接調用**!由 JVM 在新線程中調用。繼承 Thread 必須重寫;實作 Runnable 傳入構造器。 |
sleep(ms)/sleep(ms, ns) | 讓出 CPU 睡眠 | 不釋放鎖!進入 TIMED_WAITING。必須捕獲/拋出 InterruptedException。醒來後進入 RUNNABLE 等待調度,**不保證立即執行**。 |
yield() | 禮讓 CPU | **不釋放鎖**!RUNNABLE → RUNNABLE(就緒)。**僅同優先級線程有機會獲得 CPU**。不進入阻塞狀態。 |
join()/join(ms) | 等待線程終止 | 當前線程調用 t.join() 進入 WAITING/TIMED_WAITING,**釋放鎖**!底層調用 Object.wait()。t 結束或超時後當前線程恢復 RUNNABLE。 |
interrupt() | 中斷線程 | **設置中斷標誌位**;若線程處於 WAITING/TIMED_WAITING(sleep/wait/join),拋出 InterruptedException 並**清除標誌位**;若運行中,**標誌位置 true**,需自行檢查 isInterrupted() 或 Thread.interrupted()。 |
stop()/destroy() | 強制終止 | 已棄用,極不安全!會釋放所有鎖導致數據不一致,拋 ThreadDeath 錯誤。**嚴禁使用**。 |
currentThread() | 獲取當前線程 | 靜態方法,返回正在執行代碼的 Thread 實例。 |
setDaemon(true) | 設置守護線程 | **必須在 start() 前調用**。JVM 僅剩守護線程時自動退出。GC、Finalizer 為典型守護線程。 |
關鍵方法行為對比:sleep vs yield vs join vs wait
| 方法 | 釋放鎖 | 進入狀態 | 恢復條件 | 典型用途 |
sleep | 否 | TIMED_WAITING | 時間到/中斷 | 定時任務、限流延遲 |
yield | 否 | RUNNABLE (就緒) | 調度器分派 | 極少用,測試併發 |
join | 是 | WAITING/TIMED_WAITING | 目標線程結束/超時/中斷 | 主線程等待子任務完成 |
wait | 是 | WAITING/TIMED_WAITING | notify/notifyAll/中斷/超時 | 生產消費者模式、條件同步 |
四、中斷機制:優雅停止線程的標準協議
1. 中斷標誌位
- 每個 Thread 內部維護
boolean interrupted 標誌。
interrupt() 設置為 true;Thread.interrupted() 靜態方法**讀取並清除**當前線程標誌;isInterrupted() 實例方法**僅讀取**不清除。
2. 正確中斷模式(運行中線程)
// 推薦:volatile 標誌位 + 定期檢查
class Worker extends Thread {
private volatile boolean stop = false; // volatile 保證可見性
@Override
public void run() {
while (!stop && !Thread.currentThread().isInterrupted()) {
// 業務邏輯
doWork();
}
cleanup(); // 收尾工作
}
public void requestStop() {
stop = true;
interrupt(); // 雙重保險:若阻塞在 sleep/wait 可拋異常退出
}
}
3. 錯誤示範
// 錯誤 1:直接 while(true) 無檢查,interrupt() 無效
// 錯誤 2:catch InterruptedException 後不重新設置標誌或拋出,吞噬中斷
try { Thread.sleep(1000); }
catch (InterruptedException e) { /* 空 catch */ } // 中斷信號丟失!
// 正確處理
catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 重新設置標誌
// 或拋出 throw new RuntimeException(e);
}
五、Thread 狀態轉換與方法調用綜合圖
+------------------+
| NEW |
| new Thread() |
+--------+---------+
|
| start()
v
+------------------+ 時間片/調度
| RUNNABLE | <----------------------+
| (就緒/運行) | |
+--------+---------+ |
/ | \ \ |
/ | \ \ |
sleep/yield synchronized wait/join interrupt | run() 結束/異常
/ | \ \ |
v v v v v
+-----+ +-----------+ +----------+ +---------+ +-----------+
|TIMED| | BLOCKED | | WAITING | | RUNNABLE| | TERMINATED|
|WAIT | | (等鎖) | | (無限等) | | (標誌位)| | (結束) |
+-----+ +-----------+ +----------+ +---------+ +-----------+
| ^ ^ ^ ^
| timeout | acquire | notify | | interrupt()
| /interrupt | /interrupt (清除標誌)
v | | |
RUNNABLE ----+--------------+----------------+
(重新競爭 CPU) (重新競爭 CPU)
六、最佳實踐總結
- 優先實作
Runnable/Callable,配合線程池(ExecutorService),避免直接繼承 Thread 或手動 new Thread()。
- 線程命名:
new Thread(task, "Order-Processor-1"),日誌排查、監控(Arthas/VisualVM)極其關鍵。
- 守護線程謹慎使用:業務線程一般設為用戶線程(非守護),確保關鍵任務完成。
- 中斷協議標準化:
volatile stop + isInterrupted() 檢查 + InterruptedException 重新設置標誌/拋出。
- 避免
stop()/suspend()/resume(),這些已棄用 API 會導致數據損壞與死鎖。
- 併發工具類優於原生同步:
ReentrantLock、CountDownLatch、CyclicBarrier、Semaphore、CompletableFuture、ConcurrentHashMap 等 JUC 組件更安全、功能更豐富。
- 線程池參數科學配置:CPU 密集型
核心數+1,I/O 密集型 2*核心數 或根據業務壓測調整;拒絕策略選 CallerRunsPolicy 保護生產者。
掌握 Thread 類的狀態機、核心方法語義、中斷協議與上下文切換代價,是進階 Java 並發編程、排查線上卡頓/死鎖/數據不一致問題的核心內功。建議結合 jstack/Arthas thread 實戰觀察線程狀態分佈,將理論內化為診斷能力。