線程與操做系統中線程(進程)的概念同根同源,儘管千差萬別。
操做系統中有狀態以及狀態的切換,Java線程中照樣也有。
State
在Thread類中有內部類 枚舉State,用於抽象描述Java線程的狀態,共有6種不一樣的狀態
詳細定義以下:
public enum State { html
/** 編程
* 至今還沒有啓動的線程的狀態。 多線程
*/ less
NEW, spa
/** 操作系統
* 可運行線程的線程狀態。 線程
* 處於可運行狀態的某一線程正在 Java 虛擬機中運行,但它可能正在等待操做系統中的其餘資源,好比處理器。 3d
*/ htm
RUNNABLE, 對象
/**
* 受阻塞而且正在等待監視器鎖的某一線程的線程狀態。
* 處於受阻塞狀態的某一線程正在等待進入一個同步的塊/方法的監視器鎖,或者在調用 Object.wait 以後再次進入同步的塊/方法。
*/
BLOCKED,
/**
* 某一等待線程的線程狀態。
* 某一線程由於調用下列方法之一而處於等待狀態:
* 不帶超時值的 Object.wait
* 不帶超時值的 Thread.join
* LockSupport.park
* 處於等待狀態的線程正等待另外一個線程,以執行特定操做。
* 例如,已經在某一對象上調用了 Object.wait() 的線程正等待另外一個線程,以便在該對象上調用 Object.notify() 或 Object.notifyAll()。
* 已經調用了 Thread.join() 的線程正在等待指定線程終止。
*/
WAITING,
/**
* 具備指定等待時間的某一等待線程的線程狀態。
* 某一線程由於調用如下帶有指定正等待時間的方法之一而處於定時等待狀態:
* Thread.sleep
* 帶有超時值的 Object.wait
* 帶有超時值的 Thread.join
* LockSupport.parkNanos
* LockSupport.parkUntil
*/
TIMED_WAITING,
/**
* 已終止線程的線程狀態。線程已經結束執行。
*/
TERMINATED;
}
狀態詳解
NEW
當一個線程建立後,也就是new了一個Thread,那麼這個Thread的state就是NEW
有且只有這種狀況下,才爲NEW,不會從任何狀態轉換而來
也就是說若是一個線程狀態已經再也不是NEW,那麼他永遠不可能再從新回到NEW的狀態,這是一個起點
下面的示例中建立了一個線程myThread,並無調用start方法
TERMINATED
當一個線程終止後,就進入狀態TERMINATED
TERMINATED做爲線程的終點,一旦進入TERMINATED狀態,將再也不可以轉換爲其餘狀態
下面的示例中,建立了一個線程myThread,而且調用start方法啓動
而後主線程(當前線程)sleep 1秒(確保myThread確定結束了),而後查看myThread的狀態,很顯然,此時已經進入終止狀態
NEW和TERMINATED分別對應線程生命週期的起點和終點
對於NEW來講,一旦離開,就永遠回不來了;
對於TERMINATED來講,一旦到達, 就永遠回不去了;
RUNNABLE
RUNNABLE用於表示可運行狀態
下面的代碼在主線程中運行,運行過程當中是RUNNABLE狀態
API中有說到:「處於可運行狀態的某一線程正在 Java 虛擬機中運行,但它可能正在等待操做系統中的其餘資源,好比處理器。」
也就是說一個RUNNABLE並非必定正在運行
若是咱們將線程運行全部的資源與條件分爲兩種:CPU時間片以及除了時間片之外的全部其餘;
一旦進入RUNNABLE狀態,那麼他確定已經擁有了「除了時間片之外的全部其餘資源」
可是,是否正在被執行?這個不肯定,還要看是否被分配了時間片
若是沒有處理器資源(時間片)那麼就是「準備穩當」狀態,若是分配了處理器資源(時間片),那麼就是「正在運行」狀態。
因此RUNNABLE狀態能夠細分爲兩種狀態:準備穩當(READY)與RUNNING(正在運行)
可是,爲何沒有將RUNNABLE細分?
很顯然,對於開發者來講可以作到的就是「除了時間片之外的全部其餘資源」,而對於操做系統處理器CPU時間片的調度,是徹底沒有能力操控的(yield也只是提示)
因此,從應用的角度看,也就只有RUNNABLE狀態,一個RUNNABLE的線程,他隨時可能在運行,也可能在等待調度。
等待狀態
BLOCKED、WAITING、TIMED_WAITING三種狀態相對前面的幾種,相對稍微複雜一點,由於會涉及到各類切換
對照着漢字來講,這三者都有「等」的意思,可是卻又不太相同
舉幾個例子感覺一下
當你發現前方信號燈轉變爲紅燈時,你停車等待;
當你通過斑馬線時,正好有行人通過,你停車等待;
當售票窗口中午休息時,你原地等待;
這幾種等待更多的是由於不可抗力,不得不等的一種場景,BLOCKED更接近這種等待;
對於臨界資源的訪問,須要互斥訪問,Java中使用對象監視器做爲鎖,想要進入同步區域,就須要得到對應的監視器鎖
若是獲取不到,就須要等待,這就是BLOCKED狀態;
要出門時,你老婆說我化個妝,你等我下;
買水果時,售貨員說剛賣完了,師傅去倉庫去取了,您稍等一下;
此時的等,是在等一件事情的發生,WAITING更接近這種等待;
雖然都是在等待,卡住不能動,仍是等一等,仍是有區別的;
TIMED_WAITING與WAITING就比較類似了,他們的區別,從漢語的角度理解相似「你等我兩分鐘和你等一會」的區別
等兩分鐘有時間,等一下子不肯定到底等待多大一下子
再回到Java線程中,能夠認爲:
- 若是一個線程在等待獲取進入同步區域的監視器鎖,那麼是BLOCKED;
- 若是線程調用了不帶超時值的等待方法,好比 Object.wait,持續等待某件事情的發生,直到收到通知,那麼是WAITING
- 若是線程調用了好比帶有超時值的等待方法,好比wait(long timeout),進行必定時間的等待,到到時間後將再也不等待,那麼是TIMED_WAITING
狀態轉換圖
換一個角度理解,線程狀態的切換
下圖從前驅和後繼的角度分析了線程狀態的變化
以中間一列爲中心
狀態對比
既然操做系統中線程概念模型有狀態切換,Java線程也有狀態,他們有何異同?
如上圖所示,操做系統中的進程、線程模型的狀態
核心爲就緒(ready)阻塞(waiting)執行(run)
而對於Java線程中
核心狀態爲RUNNABLE、BLOCKED、WAITING、TIMED_WAITING(項目中根本不會建立線程,會藉助於線程池,因此NEW和TERMINATED非重點)
Java線程爲操做系統原生線程的映射,狀態上也是有所映射的
Runnable狀態,則對應了操做系統中的就緒(ready)和執行(run)
TIMED_WAITING ,WAITING仍是BLOCKED,對應的都是操做系統線程的阻塞(waiting)狀態
須要注意的是:這些狀態是虛擬機狀態,它不反映任何操做系統的線程狀態,能夠查看State的註釋
爲何狀態沒有對應?
咱們以前在說起線程的實現時,就有說到用戶級和內核支持的對比,內核支持的是依靠操做系統來調度的,1.2以後就是對操做系統線程的映射
因此,既然調度依賴的是操做系統,那麼,操做系統底層的狀態對於開發者來講就不是那麼必要了,由於你並不能對他進行事無鉅細的控制
JVM中的線程是操做系統底層線程的映射,既然是映射,能夠認爲是一個薄層封裝
封裝的目的是爲了更好的符合Java多線程編程的模型,而不是要原模原樣的去照搬
從這一點也能更好地理解,爲何RUNNABLE至關於READY和RUNNING,由於JVM原本就只能作到這一步,READY仍是RUNNING,搞不了,那還提供這兩個狀態幹什麼呢?
因此記住:
JVM中的狀態只是Java的多線程模型中的狀態,並不反應任何操做系統的線程狀態
JVM中的狀態與底層操做系統中線程的狀態也沒有必要去映射