執行緒的幾種狀態變化
程序是分配記憶體的單位,執行緒是CPU執行的基本單位
1.新建狀態
使用new關鍵字和某執行緒類的構造方法建立執行緒物件,則該執行緒物件處於新建狀態,表示系統已經為該執行緒物件分配了記憶體空間。處於新建狀態的執行緒可以通過start()方法使他進入就緒狀態。
2.就緒狀態
該狀態的執行緒已經具有了執行條件,進入執行緒佇列,等待系統為他分配CPU資源,一旦獲得CPU資源,該執行緒就進入執行狀態。
3執行狀態.
進入執行狀態的執行緒執行自己run()方法中的程式碼。若遇到下列情況,將終止run()方法的執行
(1)呼叫當前執行緒的stop方法或destory方法使執行緒進入死亡狀態
(2)呼叫當前執行緒的join或者wait方法進入阻塞狀態,在規定時間內可以由其他執行緒呼叫notify()或notifyall()方法將其喚醒,使之進入就緒狀態
(3)呼叫sleep()方法使執行緒進入阻塞狀態,在睡眠時間過後再次進入就緒狀態
(4)現在已棄用,suspend掛起執行緒,呼叫resume使其再次進入就緒狀態。
(5)通過呼叫yield方法方法放棄執行緒的執行,並使之進入就緒狀態。
(6)當執行緒要求I/O操作時,則進入阻塞狀態
(7)若分配給當前執行緒的CPU時間片用完,則當前執行緒進入就緒狀態。
4.阻塞狀態
一個正在執行的執行緒如果執行suspend(有死鎖風險)、join或sleep方法,或等待I/O裝置的使用權,那麼該執行緒將讓出CPU的控制權並暫停中止自己的執行,進入阻塞狀態。阻塞是他不能進入就緒佇列,只有當阻塞的原因被消除時,執行緒在可以進入就緒狀態,重新進入執行緒佇列排隊等待CPU資源,以便從原來終止處繼續執行。
(1)、等待阻塞:執行的執行緒執行wait()方法,該執行緒會釋放佔用的所有資源,JVM會把該執行緒放入“等待池”中。進入這個狀態後,是不能自動喚醒的,必須依靠其他執行緒呼叫notify()或notifyAll()方法才能被喚醒,
(2)、同步阻塞:執行的執行緒在獲取物件的同步鎖時,若該同步鎖被別的執行緒佔用,則JVM會把該執行緒放入“鎖池”中。
(3)、其他阻塞:執行的執行緒執行sleep()或join()方法,或者發出了I/O請求時,JVM會把該執行緒置為阻塞狀態。當sleep()狀態超時、join()等待執行緒終止或者超時、或者I/O處理完畢時,執行緒重新轉入就緒狀態。
5.死亡狀態
如果一個執行緒完成了全部工作或者被強制提前強制性的終止,則該執行緒處於死亡狀態。
如果,每個Java程式都有一個預設主執行緒。對於應用,主執行緒是main()方法執行的執行緒。要實現多執行緒,必須在主執行緒中中建立新的執行緒物件。Java語言使用Thread類及其子類的物件來表示執行緒。
注意:初看起來wait() 和 notify() 方法與suspend()和 resume() 方法對沒有什麼分別,但是事實上它們是截然不同的。區別的核心在於,前面敘述的suspend()及其它所有方法線上程阻塞時都不會釋放佔用的鎖(如果佔用了的話),而wait() 和 notify() 這一對方法則相反。
上述的核心區別導致了一系列的細節上的區別
首先,前面敘述的所有方法都隸屬於 Thread類,但是wait() 和 notify() 方法這一對卻直接隸屬於 Object 類,也就是說,所有物件都擁有這一對方法。初看起來這十分不可思議,但是實際上卻是很自然的,因為這一對方法阻塞時要釋放佔用的鎖,而鎖是任何物件都具有的,呼叫任意物件的 wait() 方法導致執行緒阻塞,並且該物件上的鎖被釋放。而呼叫任意物件的notify()方法則導致因呼叫該物件的 wait()方法而阻塞的執行緒中隨機選擇的一個解除阻塞(但要等到獲得鎖後才真正可執行)。
wait() 和 notify() 方法這一對方法必須在 synchronized 方法或塊中呼叫,理由也很簡單,只有在synchronized方法或塊中當前執行緒才佔有鎖,才有鎖可以釋放。同樣的道理,呼叫這一對方法的物件上的鎖必須為當前執行緒所擁有,這樣才有鎖可以釋放。因此,這一對方法呼叫必須放置在這樣的 synchronized方法或塊中,該方法或塊的上鎖物件就是呼叫這一對方法的物件。若不滿足這一條件,則程式雖然仍能編譯,但在執行時會出現IllegalMonitorStateException異常。
wait() 和 notify()方法的上述特性決定了它們經常和synchronized方法或塊一起使用,將它們和作業系統的程序間通訊機制作一個比較就會發現它們的相似性:synchronized方法或塊提供了類似於作業系統原語的功能,它們的執行不會受到多執行緒機制的干擾,而這一對方法則相當於block和wake up 原語(這一對方法均宣告為 synchronized)。它們的結合使得我們可以實現作業系統上一系列精妙的程序間通訊的演算法(如訊號量演算法),並用於解決各種複雜的執行緒間通訊問題。
關於 wait() 和 notify() 方法最後再說明兩點:
第一:呼叫notify() 方法導致解除阻塞的執行緒是從因呼叫該物件的 wait()方法而阻塞的執行緒中隨機選取的,我們無法預料哪一個執行緒將會被選擇,所以程式設計時要特別小心,避免因這種不確定性而產生問題。
第二:除了notify(),還有一個方法 notifyAll()也可起到類似作用,唯一的區別在於,呼叫 notifyAll()方法將把因呼叫該物件的 wait()方法而阻塞的所有執行緒一次性全部解除阻塞。當然,只有獲得鎖的那一個執行緒才能進入可執行狀態。
談到阻塞,就不能不談一談死鎖,略一分析就能發現,suspend()方法和不指定超時期限的wait()方法的呼叫都可能產生死鎖。遺憾的是,Java並不在語言級別上支援死鎖的避免,我們在程式設計中必須小心地避免死鎖。