在Java編程中,線程是一種重要的并發實體。為了更好地理解和管理多線程應用程序,我們需要清楚線程的不同狀態及其流轉機制。本文將詳細介紹Java中線程的幾種主要狀態以及它們之間的轉換關系。
一、線程狀態概述
Java線程的生命周期有多個狀態,主要包括以下幾種:
- 新建狀態(New)
- 就緒狀態(Runnable)
- 運行狀態(Running)
- 阻塞狀態(Blocked)
- 等待狀態(Waiting)
- 超時等待狀態(Timed Waiting)
- 終止狀態(Terminated)
每一種狀態代表著線程在其生命周期中的不同階段,下面我們將逐一解析每個狀態以及狀態之間的流轉。
二、各線程狀態詳解
1. 新建狀態(New)
當創建一個線程對象時,該線程處于新建狀態,此時線程尚未開始執行。只有當你調用 start()
方法時,新建狀態的線程才能進入就緒狀態。
示例代碼:
Thread thread = new Thread(() -> {System.out.println("線程正在執行...");
});
2. 就緒狀態(Runnable)
新建狀態的線程在調用 start()
方法后進入就緒狀態。此時線程處于等待系統調度的狀態,準備好隨時啟動執行。
示例代碼:
thread.start(); // 線程進入就緒狀態
注意:就緒狀態的線程不一定在運行,它只是等待線程調度器分配CPU時間。
3. 運行狀態(Running)
當線程被分配到CPU時,線程進入運行狀態。此時,線程正在執行它的任務。
- 線程從就緒狀態轉換為運行狀態是由線程調度器決定的。
- 運行狀態的線程可能會因為時間片用盡、調用
wait()
、sleep()
方法或由于其他線程搶占等原因而轉移到其他狀態。
4. 阻塞狀態(Blocked)
阻塞狀態的線程是因為嘗試獲取鎖(synchronized
)而被阻塞。當兩個或多個線程需要相同的資源(例如一個對象的鎖)時,只有獲得鎖的線程才能執行,而其他線程會被阻塞。
示例代碼:
synchronized (someObject) {// 執行的代碼
}
如果另一個線程已經持有 someObject
的鎖,則當前線程會進入阻塞狀態,直到能夠獲取鎖為止。
5. 等待狀態(Waiting)
線程進入等待狀態通常是由于調用了以下方法之一:
Object.wait()
Thread.join()
LockSupport.park()
在等待狀態下的線程不會被線程調度器選中執行,直到其他線程調用 notify()
、notifyAll()
或者 join()
的線程結束。
示例代碼:
synchronized (someObject) {someObject.wait(); // 進入等待狀態
}
6. 超時等待狀態(Timed Waiting)
超時等待狀態是一種特殊的等待狀態,線程等待某個條件但設定了超時時間。線程會在等待時間結束后自動恢復到就緒狀態。
例如,線程調用 Thread.sleep(millis)
或 Object.wait(millis)
。
示例代碼:
Thread.sleep(1000); // 線程等待1秒
7. 終止狀態(Terminated)
當線程的 run()
方法執行完畢,或者因為未處理異常而終止時,線程進入終止狀態。此狀態的線程無法再重新啟動。
示例代碼:
public void run() {// 執行的代碼
} // 線程執行到此,進入終止狀態
線程一旦進入終止狀態,它的生命周期就結束了。
三、線程狀態流轉
下面是一個詳細的表格,內容包括線程狀態、狀態轉換、觸發條件以及實例代碼說明。這個表格將幫助你更清晰地理解Java中線程的狀態及其轉換過程。
線程狀態 | 狀態描述 | 狀態轉換 | 觸發條件 | 示例代碼 |
---|---|---|---|---|
新建(New) | 線程對象被創建,但尚未啟動。 | 調用 start() 方法 | 調用 Thread t = new Thread() | Thread t = new Thread(); |
就緒(Runnable) | 線程已啟動,準備執行,但 CPU 未分配給它。 | 由線程調度器分配 CPU 或調用 yield() | 調用 start() 后,等待 CPU 調度 | t.start(); |
運行(Running) | 線程正在執行任務。 | 調度器分配 CPU 資源,或因時間片用盡被搶占 | CPU 調度 | // 代碼在 run() 方法中執行 |
阻塞(Blocked) | 線程等待獲取其他線程持有的鎖。 | 獲取鎖成功,或其他條件通知 | 當前線程調用了 synchronized ,但未獲取鎖 | synchronized(lock) { ... } |
等待(Waiting) | 線程等待其他線程通知。 | 由其他線程調用 notify() 或 notifyAll() | 調用 wait() 、join() | lock.wait(); |
超時等待(Timed Waiting) | 線程等待某個條件,但設定了超時時間。 | 超時結束,或被調用 notify() | 調用 Thread.sleep(millis) 或 wait(millis) | Thread.sleep(1000); |
終止(Terminated) | 線程生命周期結束。 | 無法再轉換,無論是正常結束還是異常 | run() 方法執行完畢或拋出未處理的異常 | public void run() { ... } |
狀態轉換詳細說明
-
新建(New)→ 就緒(Runnable):
- 條件:調用
start()
方法,使線程準備執行。 - 示例代碼:
Thread thread = new Thread(() -> { /* 執行的任務 */ });thread.start(); // 線程從新建狀態轉到就緒狀態
- 條件:調用
-
就緒(Runnable)→ 運行(Running):
- 條件:線程調度器將 CPU 時間片分配給線程,使其進入運行狀態。
- 示例:后臺執行的任務,調度器根據優先級選擇線程。
-
運行(Running)→ 阻塞(Blocked):
- 條件:線程嘗試訪問
synchronized
塊或方法時,發現已被其他線程占用。 - 示例代碼:
synchronized(lock) { // 訪問被鎖住的資源}
- 條件:線程嘗試訪問
-
運行(Running)→ 等待(Waiting):
- 條件:調用
wait()
、join()
或LockSupport.park()
,進入等待狀態。 - 示例代碼:
synchronized (lock) { lock.wait(); // 進入等待狀態,直到有其他線程調用notify}
- 條件:調用
-
運行(Running)→ 超時等待(Timed Waiting):
- 條件:調用帶有時間參數的方法,如
Thread.sleep(millis)
或wait(millis)
。 - 示例代碼:
Thread.sleep(1000); // 線程暫停1秒進入超時等待狀態
- 條件:調用帶有時間參數的方法,如
-
阻塞(Blocked)→ 就緒(Runnable):
- 條件:成功獲取鎖,線程重新進入就緒狀態,等待 CPU 調度。
- 示例:當鎖被釋放后,阻塞的線程嘗試獲取鎖,成功時轉為就緒狀態。
-
等待(Waiting)→ 就緒(Runnable):
- 條件:其他線程調用了
notify()
或notifyAll()
,喚醒等待線程。 - 示例代碼:
// 在另一個線程synchronized (lock) { lock.notify(); // 喚醒一個等待的線程}
- 條件:其他線程調用了
-
超時等待(Timed Waiting)→ 就緒(Runnable):
- 條件:等待超時到達,線程自動恢復就緒狀態。
- 示例:如上所示的
Thread.sleep
達到指定時間。
-
運行(Running)→ 終止(Terminated):
- 條件:線程執行完畢,或者拋出未捕獲的異常,線程自然結束。
- 示例代碼:
public void run() { // 一些代碼} // 代碼完成或拋出異常則終止
四、總結
了解Java線程的各個狀態及其流轉機制是掌握并發編程的基礎。通過清晰的狀態流轉關系,可以幫助我們更有效地管理和調試多線程應用程序。希望本文能幫助到你在多線程編程中的學習與應用。