線程與作業系統中線程(程序)的概念同根同源,盡管千差萬别。
作業系統中有狀态以及狀态的切換,Java線程中照樣也有。
State
在Thread類中有内部類 枚舉State,用于抽象描述Java線程的狀态,共有6種不同的狀态
詳細定義如下:
public enum State {
/**
* 至今尚未啟動的線程的狀态。
*/
NEW,
/**
* 可運作線程的線程狀态。
* 處于可運作狀态的某一線程正在 Java 虛拟機中運作,但它可能正在等待作業系統中的其他資源,比如處理器。
*/
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中的狀态與底層作業系統中線程的狀态也沒有必要去映射
原文位址:Java線程Thread的狀态解析以及狀态轉換分析 多線程中篇(七)