Java-多線程-線程狀態
1 簡介
Java線程並不是和Linux線程完全對等的,每個Java線程擁有NEW(新建)、RUNNABLE(就緒)、BLOCKED(阻塞)、WAITING(等待)、TIMED WAITING(計時等待)、TERMINATED(終止)。
Java線程調度屬於搶佔式調度,線程競爭CPU時間分片來執行,一個線程運行幾十毫秒中就處於RUNNING狀態,而時間片用完了被剝奪CPU資源又處於READY狀態了,等待下次調度。
2 線程狀態
-
NEW(新建)
使用new Thread(new Runnable)
類或其子類建立一個線程對象後,該線程對象就處於新建狀態。此時線程還未執行,一般是在構造方法中做一些初始化工作。
NEW狀態的線程只能在
thread.start()
方法之後轉爲RUNNABLE狀態。 -
RUNNABLE(就緒)
NEW狀態的線程調用了thread.start()
方法之後,該線程就進入RUNNABLE狀態,等待JVM裏線程調度器的調度,此時處於READY就緒狀態。當獲得CPU時間片後,線程開始執行,線程處於RUNNING運行狀態。時間片用完後,線程被剝奪運行資格,等待下一次運行,又變爲就緒狀態。也就是說,此狀態下不能保證線程是就緒還是正在運行。
-
BLOCKED(阻塞)
RUNNABLE狀態線程申請被其他線程持有的對象鎖,此時就轉爲BLOCKED
狀態,此時線程不運行任何代碼且消耗最少的資源,直到線程調度器重新激活運行該線程。當其他線程釋放該對象鎖後,如果本線程競爭到了對象鎖,就轉回
RUNNABLE
狀態。 -
WAITING(無期限等待)
如果一個線程A等待另一個線程給與調度器一個條件時,線程A進入WAITING
狀態,等待被其他線程notify喚醒。如調用
Object.wait
、Thread.join
、java.util.concurrent
中的Lock或Condition等。條件發生後,就轉回
RUNNABLE
狀態。(如果是wait方法進入的等待狀態,被notify喚醒後進入對象鎖同步隊列重新競爭鎖,競爭到了就進入RUNNABLE狀態,競爭失敗就進入BLOCKED狀態) -
TIMED WAITING(計時等待)
以上方法有一些帶有時間參數的重載方法(如Object.wait
、Thread.join
、java.util.concurrent
中的Lock.tryLock、Condition.await),就會進入TIMED WAITING
狀態條件發生後,就轉回`RUNNABLE`狀態(如果是wait方法進入的等待狀態,被notify喚醒後進入對象鎖同步隊列重新競爭鎖,競爭到了就進入RUNNABLE狀態,競爭失敗就進入BLOCKED狀態)。
-
TERMINATED(終止)
線程run方法結束或拋出了未捕獲的異常導致結束。在一個TERMINATED的線程上調用start()方法會拋出
java.lang.IllegalThreadStateException
異常。
3 相關方法
-
Thread.sleep(long millis)
由當前線程調用此方法,調用後進入TIMED_WAITING
狀態,但不釋放對象鎖。millis期滿後線程自動喚醒進入RUNNABLE狀態。 -
Thread.yield(),
由當前線程調用此方法,給CPU提示表示當前線程願意放棄獲取的CPU時間片(調度器可忽略該提示),但不釋放對象鎖。如果成功釋放時間片則該線程由運行狀態變爲就緒狀態,由調度器再次選擇線程進行調度執行。- Sleep對比Yield
主要是使用場景不同。Sleep主要用來指定睡眠時間讓出CPU,並在時間到後再被系統分配CPU資源,進行調度執行;而Yield是用來主動釋放CPU給其他線程執行,但無法精確控制是否釋放。
- Sleep對比Yield
-
t.join()/t.join(long millis),
join方法主要用來等待其他線程運行結束,再繼續運行自己的線程代碼。當前線程裏調用其它線程t的join方法,當前線程進入
WAITING
/TIMED_WAITING
狀態,當前線程不會釋放已經持有的對象鎖。等到線程t執行完畢或者millis時間到,當前線程重新進入
RUNNABLE
狀態。 -
obj.wait()
當前線程調用對象的wait()方法,當前線程會釋放該對象鎖,並進入等待隊列等待其他競爭到鎖的線程執行完後喚醒,重新進入等待隊列進行鎖競爭,競爭成功後纔會從wait方法返回。注意這個wait方法只會讓該線程釋放當前Object的對象鎖,而不會放棄擁有的其他對象鎖!
-
obj.wait(mills)
當前線程調用對象的wait()方法,當前線程會釋放該對象鎖,並進入等待隊列等待其他競爭到鎖的線程執行完後喚醒或時間耗盡,重新進入等待隊列進行鎖競爭,競爭成功後纔會從wait方法返回。注意這個wait方法只會讓該線程釋放當前Object的對象鎖,而不會放棄擁有的其他對象鎖!
-
obj.notify()
該方法用來任意喚醒一個在對象鎖的等待集的線程(其實看了源碼會發現不是任意的,而是一個WaitQueue,FIFO)。 -
obj.notifyAll()
喚醒在此對象Monitor上等待的所有線程。
4 線程優先級
很多操作系統都提供線程優先級的概念,但是由於平臺特性的問題,Java中的線程優先級和不同平臺中系統線程優先級並不匹配,所以Java線程優先級可以僅僅理解爲“建議優先級”,通俗來說就是java.lang.Thread#setPriority(int newPriority)
並不一定生效,有可能Java線程的優先級會被系統自行改變。