線程有幾種狀態(tài),狀態(tài)之間的流轉(zhuǎn)是怎樣的?
Java 中的線程有六種狀態(tài),這些狀態(tài)定義在 Thread.State 枚舉中。以下是這六種狀態(tài)及其之間的流轉(zhuǎn)關(guān)系:
線程的六種狀態(tài)
- NEW(新建狀態(tài)):
線程在創(chuàng)建后,但還沒有調(diào)用 start() 方法時,處于 NEW 狀態(tài)。
線程對象被實例化,但還未開始執(zhí)行。
- RUNNABLE(可運行狀態(tài)):
線程調(diào)用了 start() 方法后,進入 RUNNABLE 狀態(tài)。
線程可能正在執(zhí)行(CPU 正在執(zhí)行該線程),也可能是就緒狀態(tài),等待操作系統(tǒng)調(diào)度執(zhí)行。注意:在 Java 中,RUNNABLE 既包括了線程在運行的狀態(tài),也包括了線程準備好可以運行但暫時未被操作系統(tǒng)調(diào)度的狀態(tài)。
BLOCKED(阻塞狀態(tài)):
線程處于 BLOCKED 狀態(tài)時,表示它正在等待獲取某個鎖,以便執(zhí)行接下來的操作。
這種狀態(tài)通常發(fā)生在多個線程競爭同一資源(如鎖)時。線程在等待獲得鎖時被阻塞,直到鎖可用。
WAITING(等待狀態(tài)):
線程調(diào)用了 Object.wait()。
線程調(diào)用了 Thread.join()(沒有指定超時)。
線程調(diào)用了 LockSupport.park()。
線程進入 WAITING 狀態(tài)時,表示它正在等待其他線程的某個操作來喚醒它,且沒有設(shè)定超時限制。
常見的情況是:
TIMED_WAITING(定時等待狀態(tài)):
線程調(diào)用了 Thread.sleep(milliseconds)。
線程調(diào)用了 Object.wait(milliseconds)。
線程調(diào)用了 Thread.join(milliseconds)。
線程調(diào)用了 LockSupport.parkNanos() 或 LockSupport.parkUntil()。
線程進入 TIMED_WAITING 狀態(tài)時,表示它正在等待某個條件滿足,但等待是有超時限制的。
線程會等待一段時間,超時后會自動被喚醒。
常見的情況是:
TERMINATED(終止狀態(tài)):
線程在執(zhí)行完畢后,進入 TERMINATED 狀態(tài)。
線程的生命周期結(jié)束,線程對象不能再被啟動。
線程因為正常完成任務,或者由于異常等原因結(jié)束時都會進入這個狀態(tài)。
狀態(tài)之間的流轉(zhuǎn)
- NEW → RUNNABLE
調(diào)用 Thread.start() 方法后,線程從 NEW 狀態(tài)變?yōu)?RUNNABLE 狀態(tài)。
- RUNNABLE → BLOCKED
線程試圖獲取已被其他線程持有的監(jiān)視器鎖時,會從 RUNNABLE 狀態(tài)變?yōu)?BLOCKED 狀態(tài)。
BLOCKED → RUNNABLE
當線程獲取到所需的監(jiān)視器鎖時,會從 BLOCKED 狀態(tài)變?yōu)?RUNNABLE 狀態(tài)。
RUNNABLE → WAITING
線程調(diào)用 Object.wait(), Thread.join(), LockSupport.park() 等方法后,會從 RUNNABLE 狀態(tài)變?yōu)?WAITING 狀態(tài)。
RUNNABLE → TIMED_WAITING
線程調(diào)用 Thread.sleep(long millis), Object.wait(long timeout), Thread.join(long millis), LockSupport.parkNanos(long nanos), LockSupport.parkUntil(long deadline) 等方法后,會從 RUNNABLE 狀態(tài)變?yōu)?TIMED_WAITING 狀態(tài)。
WAITING → RUNNABLE
當?shù)却龡l件滿足時,例如 Object.notify(), Object.notifyAll(), Thread.interrupt() 等方法被調(diào)用后,線程會從 WAITING 狀態(tài)變?yōu)?RUNNABLE 狀態(tài)。
TIMED_WAITING → RUNNABLE
當?shù)却龝r間到期或等待條件滿足時,線程會從 TIMED_WAITING 狀態(tài)變?yōu)?RUNNABLE 狀態(tài)。
RUNNABLE → TERMINATED
當線程的 run() 方法正常結(jié)束或拋出未捕獲的異常時,線程會從 RUNNABLE 狀態(tài)變?yōu)?TERMINATED 狀態(tài)。
圖解
+--------+ start() +-----------+
| NEW | ------------> | RUNNABLE |
+--------+ +-----------+
| |
+---+ +---+
| |
v v
+-----------+ +---------------+
| BLOCKED | | WAITING |
+-----------+ +---------------+
| |
+---+ +---+
| |
v v
+-----------------+
| TIMED_WAITING |
+-----------------+
|
+---+
|
v
+-----------+
| TERMINATED|
+-----------+
總結(jié):
- NEW:線程尚未啟動。
- RUNNABLE:線程正在等待 CPU 調(diào)度執(zhí)行,可能正在運行,也可能等待 CPU 資源。
- BLOCKED:線程正在等待獲取鎖。
- WAITING:線程正在等待其他線程的通知(沒有超時)。
- TIMED_WAITING:線程正在等待某個條件滿足,并且設(shè)置了超時。
- TERMINATED:線程已經(jīng)執(zhí)行完畢并結(jié)束。
WAITING 和 TIMED_WAITING 的區(qū)別?
- WAITING 是無條件的等待,線程會一直等待直到被其他線程喚醒。
- TIMED_WAITING 是帶有超時的等待,線程會等待一段時間,超時后自動喚醒。
這兩種狀態(tài)的關(guān)鍵區(qū)別是 是否有超時限制,WAITING 沒有時間限制,而 TIMED_WAITING 是有超時的。
為什么線程沒有RUNNING狀態(tài)?
因為它與 RUNNABLE 狀態(tài)的概念重合。
具體原因:
- RUNNABLE 狀態(tài)包括正在執(zhí)行和就緒的線程:
在 Java 的線程狀態(tài)模型中,RUNNABLE 狀態(tài)既包括了線程已經(jīng)在 CPU 上執(zhí)行的狀態(tài)(實際上正在運行),也包括了線程處于就緒隊列中,等待 CPU 分配時間片的狀態(tài)。
線程一旦被操作系統(tǒng)調(diào)度執(zhí)行,它就進入 RUNNABLE 狀態(tài),但這個狀態(tài)的定義并不區(qū)分是否正在執(zhí)行。操作系統(tǒng)負責將線程從就緒隊列中調(diào)度到實際的執(zhí)行狀態(tài),并且 Java 并不會對這個過程做細分。
- 線程狀態(tài)模型的設(shè)計:
Java 線程狀態(tài)模型的設(shè)計簡化了線程的生命周期管理,沒有單獨為運行中的線程定義一個狀態(tài)。RUNNABLE 狀態(tài)作為一個集合狀態(tài),能夠涵蓋線程既可能在執(zhí)行也可能在等待 CPU 資源的場景。
在操作系統(tǒng)中,線程的調(diào)度是由操作系統(tǒng)內(nèi)核進行的,Java 并不直接控制線程的執(zhí)行時間或如何選擇線程,而是通過 Java 的線程調(diào)度器(JVM)委托給操作系統(tǒng)。所以,線程一旦在運行,實際上是 "正在運行" 或 "就緒等待" 都在 RUNNABLE 狀態(tài)下進行管理。
JVM 調(diào)度與操作系統(tǒng)的區(qū)別:
線程的生命周期與操作系統(tǒng)的調(diào)度密切相關(guān)。在 Java 中,線程進入 RUNNABLE 狀態(tài)意味著它已經(jīng)準備好由操作系統(tǒng)調(diào)度,但操作系統(tǒng)可能會把它從 CPU 上切換出去,導致線程不再“運行”。
JVM 不強制區(qū)分 運行中 和 就緒中,因為從 JVM 的角度看,線程要么處于 RUNNABLE 狀態(tài),要么因為某種原因(如阻塞、等待等)不再處于運行狀態(tài)。