執行緒、程序的狀態
執行緒的狀態
1、新建狀態:新建立一個執行緒物件。
2、就緒狀態:執行緒物件建立後,其他執行緒呼叫了該物件的start()方法。該狀態的執行緒位於“可執行的執行緒池”中,變得可執行,只等待獲取CPU的使用權。即在就緒狀態的程序除了CPU之外,其他的執行所需資源都已全部獲得。
2.1、就緒狀態只是說有資格執行,排程程式沒有挑選到你,你就永遠是可執行狀態。
2.2、呼叫執行緒的start()方法,此執行緒進入就緒狀態。
2.3、當前執行緒sleep()方法結束,其他執行緒join()結束,等待使用者輸入完畢,某個執行緒拿到物件鎖,這些執行緒也將進入就緒狀態。
2.4、當前執行緒時間片用完了,呼叫當前執行緒的yeild()方法,當前執行緒進入就緒方法。
2.5、鎖池裡的執行緒拿到物件鎖後,進入就緒狀態。
3、執行狀態:就緒狀態的執行緒獲取了CPU,執行程式程式碼。
3.1、run()方法或main()方法結束後,執行緒就進入終止狀態。
3.2、當執行緒呼叫了自身的sleep()方法或其他執行緒的join()方法,程序讓出CPU,然後就會進入阻塞狀態(該狀態即停止當前執行緒,但並不釋放所佔有的資源即呼叫sleep()方法後,執行緒不會釋放它的”鎖標誌“)。當sleep()結束或join()結束後,該執行緒進入就緒狀態,繼續等待OS分配CPU時間片。典型的,sleep()被用在等待某個資源就緒的情形:測試發現條件不滿足後,讓執行緒阻塞一段時間後重新測試,直到條件滿足為止。
3.3、執行緒呼叫了yield方法,意思是放棄當前獲得的CPU時間片,回到就緒狀態,這是與其他程序處於同等競爭狀態,OS有可能會接著讓這個程序進入執行狀態,呼叫yield()的效果等價於排程程式認為該執行緒已執行了足夠的時間片從而需要轉到另一個執行緒。yield()只是使當前執行緒重新回到可執行狀態,所以執行yield()的執行緒有可能在進入到可執行狀態後馬上又被執行。
3.4、當執行緒進入就緒狀態,發現將要呼叫的資源被synchronized,獲取不到鎖標記,將會立即進入鎖池狀態,等待獲取鎖標記(這是的鎖池裡也許已經有了其他執行緒在等待獲取所標記,這時它們處於佇列狀態,即先到先得),一旦執行緒獲得鎖標記後,就轉入就緒狀態,等待OS分配時間片。
3.5、suspend()和resume()方法:兩個方法配套使用,suspend()使得執行緒進入阻塞狀態,並且不會自動恢復,必須被對應的resume()呼叫,才能使得執行緒重新進入可執行狀態。典型的,suspend()和resume()被用在等待另一個執行緒產生的結果的情形:測試發現結果還沒有產生後,讓執行緒阻塞,另一個執行緒產生看了結果後,呼叫resume()使其恢復。
3.6、wait()和notify()方法:當執行緒呼叫wait()方法後會進入等待佇列(進入這個狀態會釋放所佔有的所有資源,與阻塞狀態不同),進入這個狀態後,是不能自動喚醒的,必須依靠其他執行緒呼叫notify()或notifyAll()方法才能被喚醒(由於notify()只是喚醒一個執行緒,但我們不能確定具體喚醒的是哪一個執行緒,也許我們需要喚醒的執行緒不能夠被喚醒,因此在實際使用時,一般都用notifyAll()方法,喚醒所有執行緒),執行緒被喚醒後會進入鎖池,den
4、阻塞狀態:阻塞狀態是執行緒因為某種原因放棄CPU使用權,暫時停止執行。直到執行緒進入就緒狀態,才有機會轉到執行狀態。
阻塞的情況分三種:
4.1、等待阻塞:執行的執行緒執行wait()方法,該執行緒會釋放佔用的所有資源,JVM會把該執行緒放入”等待池“中。進入這個狀態後,是不能自動喚醒的,必須依靠其他執行緒呼叫notify()或者notifyAll()方法才能被喚醒。
4.2、同步阻塞:執行的執行緒在獲取物件的同步鎖時,若該同步鎖被別的執行緒佔用,則JVM會把該執行緒放入”鎖池“中。
4.3、其他阻塞:執行的執行緒執行了sleep()或join()方法,或者發出了i/o請求時,JVM會把該執行緒置為阻塞狀態。當sleep()狀態超時、join()等待執行緒終止或者超時、或者i/o處理完畢時,執行緒重新轉入就緒狀態。
4.4、當執行緒剛進入可執行狀態(注意,還沒執行),發現將要呼叫的資源被synchroniza(同步),獲取不到鎖標記,將會立即進入鎖池狀態,等待獲取鎖標記(這時的鎖池裡也許已經有了其他執行緒在等待獲取鎖標記,這時它們處於佇列狀態,既先到先得),一旦執行緒獲得鎖標記後,就轉入就緒狀態,等待OS分配CPU時間片;
4.5. suspend() 和 resume()方法:兩個方法配套使用,suspend()使得執行緒進入阻塞狀態,並且不會自動恢復,必須其對應的resume()被呼叫,才能使得執行緒重新進入可執行狀態。典型地,suspend()和 resume() 被用在等待另一個執行緒產生的結果的情形:測試發現結果還沒有產生後,讓執行緒阻塞,另一個執行緒產生了結果後,呼叫 resume()使其恢復。
4.6、wait()和 notify() 方法:當執行緒呼叫wait()方法後會進入等待佇列(進入這個狀態會釋放所佔有的所有資源,與阻塞狀態不同),進入這個狀態後,是不能自動喚醒的,必須依靠其他執行緒呼叫notify()或notifyAll()方法才能被喚醒(由於notify()只是喚醒一個執行緒,但我們由不能確定具體喚醒的是哪一個執行緒,也許我們需要喚醒的執行緒不能夠被喚醒,因此在實際使用時,一般都用notifyAll()方法,喚醒有所執行緒),執行緒被喚醒後會進入鎖池,等待獲取鎖標記。
5、死亡狀態:執行緒執行完了或者因異常退出了run()方法,該執行緒結束生命週期。
程序的狀態
程序是系統進行資源分配和排程的基本單位。執行緒是CPU排程的基本單位。
一般來說,程序有三個狀態:
1、執行態:程序佔用CPU,並在CPU上執行。
2、就緒態:程序已經具備執行條件,但是CPU還沒有分配過來。
3、阻塞態:程序因等待某件事發生而暫時不能執行。
上述的三種狀態之間轉換分為六種情況:
1、執行——>就緒:主要是程序佔用CPU的時間過長,而系統分配給該程序佔用CPU的時間是有限的;在採用搶先式優先順序排程演算法的系統中,當有更高優先順序的程序要執行時,該程序就被迫讓出CPU,該程序便由執行狀態轉變為就緒狀態。2、就緒——>執行:執行的程序的時間片用完,排程就轉到就緒佇列中選擇合適的程序分配CPU
3、執行——>阻塞:正在執行的程序因發生某等待事件而無法執行,則程序由執行狀態變為阻塞狀態,如發生了I/O請求
4、阻塞——>就緒:程序所等待的事件已經發生,就進入就緒佇列
以下兩種狀態是不可能發生的:
1、阻塞——>執行:即使給阻塞程序分配CPU,也無法執行,作業系統在進行排程時不會從阻塞佇列進行挑選,而是從就緒佇列中選取2、就緒——>阻塞:就緒態根本就沒有執行,談不上進入阻塞態。
在一些系統中,又增加了一些新狀態,如掛起狀態,可執行狀態,深度睡眠狀態,淺度睡眠狀態,暫停狀態,僵死狀態。
1、可執行狀態:執行狀態和就緒狀態的合併,表示程序正在執行或準備執行,Linux 中使用TASK_RUNNING 巨集表示可執行狀態。2、淺度睡眠狀態:程序正在睡眠(被阻塞),等待資源的到來是喚醒,也可以通過其他程序訊號或時鐘中斷喚醒,進入執行佇列。Linux 中使用TASK_INTERRUPTIBLE 巨集表示此狀態。
3、深度睡眠狀態:其和淺度睡眠基本類似,但不可被其他程序訊號或時鐘中斷喚醒。Linux 中使用TASK_UNINTERRUPTIBLE 巨集表示此狀態。
4、暫停狀態:程序暫停執行接受某種處理。Linux 使用TASK_STOPPED 巨集表示此狀態。
5、僵死狀態:程序已經結束但未釋放程序控制塊(PCB),Linux 使用TASK_ZOMBIE 巨集表示此狀態。
掛起狀態:在執行狀態的程序通過掛起即可進入就緒狀態,如圖所示,就緒狀態和阻塞狀態都分為活動態和靜止態。由活動態向靜止態轉換就是通過掛起實現的。
引入掛起狀態的原因有:
1、終端使用者的請求。當終端使用者在自己的程式執行期間發現有可疑問題時,希望暫時使自己的程式靜止下來。亦即,使正在執行的程序暫停執行;若此時使用者程序正處於就緒狀態而未執行,則該程序暫不接受排程,以便使用者研究其執行情況或對程式進行修改。我們把這種靜止狀態稱為掛起狀態。
2、父程序請求。有時父程序希望掛起自己的某個子程序,以便考查和修改該子程序,或者協調各子程序間的活動。
3、負荷調節的需要。當實時系統中的工作負荷較重,已可能影響到對實時任務的控制時,可由系統把一些不重要的程序掛起,以保證系統能正常執行。
4、作業系統的需要。作業系統有時希望掛起某些程序,以便檢查執行中的資源使用情況或進行記賬。