Java線程生命周期包含六種狀態,分別是new、runnable、blocked、waiting、timed_waiting和terminated。1. new表示線程被創建但尚未啟動;2. runnable表示線程已就緒或正在運行;3. blocked表示線程因等待鎖而阻塞;4. waiting表示線程無限期等待其他線程操作;5. timed_waiting表示線程在指定時間內等待;6. terminated表示線程執行完畢或異常終止。理解這些狀態有助于診斷并發問題并優化性能,例如通過jstack分析線程堆棧信息判斷狀態,同時避免死鎖需破壞互斥、占有等待、不可剝奪或循環等待條件之一。
Java中的線程狀態可以理解為線程在其生命周期中所處的不同階段,從創建到消亡,線程會經歷多種狀態的轉變。理解這些狀態對于編寫高效、穩定的并發程序至關重要。
解決方案
Java線程的生命周期包含六種狀態:NEW(新建)、RUNNABLE(可運行)、BLOCKED(阻塞)、WAITING(等待)、TIMED_WAITING(定時等待)和TERMINATED(終止)。
立即學習“Java免費學習筆記(深入)”;
Java線程狀態詳解:深入理解并發編程的基石
線程狀態是理解Java并發編程的關鍵。不同的狀態反映了線程與操作系統、鎖、以及其他線程之間的交互情況。深入理解這些狀態,能幫助我們更好地診斷并發問題,優化程序性能。
-
NEW (新建):線程被創建但尚未啟動。此時線程對象已經存在,但尚未調用 start() 方法。
-
RUNNABLE (可運行):這是一個復合狀態,包括了 READY (就緒) 和 RUNNING (運行中) 兩種狀態。READY狀態表示線程已經準備好運行,等待CPU調度;RUNNING狀態表示線程正在執行 run() 方法中的代碼。由于操作系統調度的不確定性,線程在這兩種狀態之間切換是無法人為控制的。
-
BLOCKED (阻塞):線程在等待獲取鎖時進入阻塞狀態。例如,當線程嘗試進入一個被其他線程持有的 synchronized 塊或方法時,就會進入 BLOCKED 狀態。
-
WAITING (等待):線程無限期地等待另一個線程執行特定操作。進入 WAITING 狀態的常見方式有:
線程可以通過以下方式退出 WAITING 狀態:
- 被 notify() 或 notifyAll() 方法喚醒。
- 被中斷(interrupt() 方法)。
- Thread.join() 方法的等待時間結束。
-
TIMED_WAITING (定時等待):與 WAITING 狀態類似,但線程會等待指定的時間。進入 TIMED_WAITING 狀態的常見方式有:
- 調用 Thread.sleep() 方法。
- 調用 Object.wait(long timeout) 方法(帶超時參數)。
- 調用 Thread.join(long timeout) 方法(帶超時參數)。
- 調用 LockSupport.parkNanos(long nanos) 或 LockSupport.parkUntil(long deadline) 方法。
線程可以通過以下方式退出 TIMED_WAITING 狀態:
- 等待時間結束。
- 被 notify() 或 notifyAll() 方法喚醒。
- 被中斷(interrupt() 方法)。
- Thread.join() 方法的等待時間結束。
-
TERMINATED (終止):線程執行完畢或因異常而終止。此時線程已經結束生命周期,不能再次啟動。
如何使用jstack命令分析線程狀態?
jstack 是一個非常有用的命令行工具,它可以打印出指定 Java 進程的線程堆棧信息。通過分析線程堆棧信息,我們可以了解線程當前的狀態,以及線程正在執行的代碼。這對于診斷死鎖、線程阻塞等并發問題非常有幫助。
例如,要分析進程ID為1234的Java進程,可以執行以下命令:
jstack 1234
jstack 的輸出會包含每個線程的堆棧信息,其中包括線程的狀態。例如:
"Thread-1" #10 prio=5 os_prio=0 tid=0x00007f9b88888000 nid=0x1a03 waiting on condition [0x00007f9b87e7f000] java.lang.Thread.State: TIMED_WAITING (sleeping) at java.lang.Thread.sleep(Native Method) at com.example.MyThread.run(MyThread.java:10) at java.lang.Thread.run(Thread.java:745)
從上面的輸出可以看出,線程 “Thread-1” 的狀態是 TIMED_WAITING (sleeping),并且正在執行 Thread.sleep() 方法。
死鎖是如何產生的?如何避免?
死鎖是指兩個或多個線程互相等待對方釋放資源,導致所有線程都無法繼續執行的現象。死鎖的產生通常需要滿足以下四個條件:
- 互斥條件:資源必須處于獨占狀態,即一次只能被一個線程持有。
- 占有且等待條件:線程已經持有一個資源,但又請求新的資源,并且在等待新資源的同時,不釋放已經持有的資源。
- 不可剝奪條件:線程已經獲得的資源,在未使用完之前,不能被其他線程強行剝奪。
- 循環等待條件:多個線程之間形成循環等待資源的關系。
要避免死鎖,可以破壞上述任何一個條件。常見的避免死鎖的方法有:
- 避免嵌套鎖:盡量避免在一個鎖的范圍內請求另一個鎖。
- 使用定時鎖:使用 tryLock(long timeout, TimeUnit unit) 方法,在等待鎖的時候設置超時時間,避免無限期等待。
- 資源排序:為所有資源定義一個全局的順序,線程按照順序請求資源,避免循環等待。
- 使用死鎖檢測工具:一些工具可以幫助檢測死鎖,例如 jconsole。
線程狀態轉換圖的實際應用:優化并發程序
理解線程狀態轉換圖對于優化并發程序至關重要。例如,如果發現大量線程處于 BLOCKED 狀態,可能意味著鎖競爭激烈,需要優化鎖的使用方式。如果發現線程頻繁在 WAITING 和 RUNNABLE 狀態之間切換,可能意味著線程需要等待的條件過于頻繁,需要重新設計線程間的協作方式。
通過分析線程狀態,我們可以更好地理解程序的并發行為,從而找到性能瓶頸,并進行優化。
代碼示例:模擬線程狀態轉換
以下代碼演示了線程在不同狀態之間的轉換:
public class ThreadStateDemo { private static final Object lock = new Object(); public static void main(String[] args) throws InterruptedException { // NEW 狀態 Thread thread = new Thread(() -> { try { // RUNNABLE -> BLOCKED 狀態 synchronized (lock) { System.out.println("Thread acquired lock."); // RUNNABLE -> WAITING 狀態 lock.wait(); System.out.println("Thread woke up."); } } catch (InterruptedException e) { e.printStackTrace(); } }); System.out.println("Thread state: " + thread.getState()); // 輸出 NEW thread.start(); // NEW -> RUNNABLE Thread.sleep(100); System.out.println("Thread state: " + thread.getState()); // 可能輸出 RUNNABLE 或 BLOCKED // 喚醒線程 synchronized (lock) { lock.notify(); } Thread.sleep(100); System.out.println("Thread state: " + thread.getState()); // 可能輸出 RUNNABLE thread.join(); // 等待線程結束 System.out.println("Thread state: " + thread.getState()); // 輸出 TERMINATED } }
這段代碼創建了一個線程,并演示了線程從 NEW 狀態到 RUNNABLE 狀態,再到 BLOCKED 狀態,最后到 WAITING 狀態的轉換。通過運行這段代碼,可以更直觀地理解線程狀態的轉換過程。