摘要: 原創出處 https://studyidea.cn 「公衆號:程序通事 」歡迎關注和轉載,保留摘要,謝謝!
使用 Java 阻塞 I/O 模型讀取數據,將會致使線程阻塞,線程將會進入休眠,從而讓出 CPU 的執行權,直到數據讀取完成。這個期間若是使用 jstack 查看線程狀態,卻能夠發現Java 線程狀態是處於 RUNNABLE,這就和上面說的存在矛盾,爲何會這樣?html
上面的矛盾實際上是混淆了操做系統線程狀態與 Java 線程狀態。這裏說的線程阻塞進入休眠狀態,實際上是操做系統層面線程實際狀態。而咱們使用 jstack 查看的線程狀態倒是 JVM 中的線程狀態。java
線程是操做系統中一種概念,Java 對其進行了封裝,Java 線程本質上就是操做系統的中線程,其狀態與操做系統的狀態大體相同,但仍是存在一些區別。面試
下面首先來看咱們熟悉的 Java 線程狀態。併發
Java 線程狀態定義在 Thread.State
枚舉中,使用 thread#getState
方法能夠獲取當前線程的狀態。ide
Thread.State
狀態以下圖:idea
能夠看到 Java 線程總共存在 6 中狀態,分別爲:spa
NEW(初始狀態)與 RUNNABLE(運行狀態)操作系統
每一個使用 new Thread()
剛建立出線程實例狀態處於 NEW 狀態,一旦調用 thread.start()
,線程狀態將會變成 RUNNABLE。線程
RUNNABLE(運行狀態) 與 BLOCKED(阻塞狀態)code
RUNNABLE 狀態的線程在進入由 synchronized
修飾的方法或代碼塊前將會嘗試獲取一把隱式的排他鎖,一旦獲取不到,線程狀態將會變成 BLOCKED,等待獲取鎖。一旦有其餘線程釋放這把鎖,線程成功搶到該鎖,線程狀態就將會從 BLOCKED 轉變爲 RUNNABLE 狀態。
RUNNABLE(運行狀態) 與 WATTING(等待狀態)
處於 WATTING 狀態的線程將會一直處於無限期的等待狀態,須要等待其餘線程喚醒。總共存在三種方法將會使線程從 RUNNABLE 變成 WATTING。
Object#wait
線程在獲取到 synchronized
隱式鎖後,顯示的調用 Object#wait()
方法。這種狀況下該線程將會讓出隱式鎖,一旦其餘線程獲取到該鎖,且調用了 Object.notify()
或object.notifyAll()
,線程將會喚醒,而後變成 RUNNABLE。
Thread#join
join
方法是一種線程同步方法。假設咱們在 main 方法中執行 Thread A.join() 方法,main 線程狀態就會變成 WATTING。直到 A 線程執行完畢,main 線程纔會再變成 RUNNABLE。
LockSupport#park()
LockSupport 是 JDK 併發包裏重要對象,不少鎖的實現都依靠該對象。一旦調用 LockSupport#park()
,線程就將會變爲 WATTING 狀態。若是須要喚醒線程就須要調用 LockSupport#unpark,而後線程狀態從新變爲 RUNNABLE。
RUNNABLE(運行狀態) 與 TIMED_WAITING(限時等待狀態)
TIMED_WAITING 與 WATTING 功能同樣,只不過前者增長限時等待的功能,一旦等待時間超時,線程狀態自動變爲 RUNNABLE。如下幾種狀況將會觸發這種狀態:
Thread#sleep(long millis)
Object.wait (long timeout)
方法Thread#join (long millis)
LockSupport#parkNanos (Object blocker, long deadline)
LockSupport#parkUntil (long deadline)
RUNNABLE(運行狀態)與 TERMINATED(終止狀態)
線程一旦執行結束或者線程執行過程發生異常且未正常捕獲處理,狀態都將會自動變成 TERMINATED。
Java 線程 6 種狀態看起來挺複雜的,但其實上面 BLOCKED,WATTING,TIMED_WAITING,都會使線程處於休眠狀態,因此咱們將這三類都歸類爲休眠狀態。這麼分類的話,Java 線程生命週期就能夠簡化爲下圖:
上面講完 Java 系統的線程狀態,咱們來看下通用操做系統的線程狀態。操做系統線程狀態能夠分爲初始狀態,可運行狀態,運行狀態,休眠狀態以及終止狀態,以下圖:
這 5 中狀態詳細狀況以下:
比較 Java 線程與操做系統線程,能夠發現 Java 線程狀態沒有可運行狀態。也就是說 Java 線程 RUNNABLE 狀態包括了操做系統的可運行狀態與運行狀態。一個處於 RUNNABLE 狀態 Java 線程,在操做系統層面狀態可能爲可運行狀態,正在等待系統分配 CPU 使用權。
另外 Java 線程細分了操做系統休眠狀態,分紅了 BLOCKED,WATTING,TIMED_WAITING 三種。
當線程調用阻塞式 API,線程進入休眠狀態,這裏指的是操做系統層面的。從 JVM 層面,Java 線程狀態依然處於 RUNNABLE 狀態。JVM 並不關心操做系統線程實際狀態。從 JVM 看來等待 CPU 使用權(操做系統線程狀態爲可運行狀態)與等待 I/O (操做系統線程狀態處於休眠狀態)沒有區別,都是在等待某種資源,因此都納入 RUNNABLE 狀態。
其餘 Java 線程狀態與操做線程狀態相似。