進程的七狀態模型
進程的基本狀態:
(1)運行:已經獲得必要的資源 占用處理機 處理機正在執行該進程
(2)就緒:進程等待分配CPU
(3)阻塞:等待某個事件
- 運行——>就緒:1,主要是進程占用CPU的時間過長,時間片用完;
在采用搶先式優先級調度算法的系統中,當有更高優先級的進程要運行時,該進程就被迫讓出CPU,該進程便由執行狀態轉變為就緒狀態。
-
就緒——>運行:運行的進程的時間片用完,調度就轉到就緒隊列中選擇合適的進程分配CPU
-
運行——>阻塞:正在執行的進程因發生某等待事件而無法執行,則進程由執行狀態變為阻塞狀態,如發生了I/O請求
-
阻塞——>就緒: 進程所等待的事件已經發生,就進入就緒隊列
此時進程的五狀態模型如下圖所示:
掛起狀態
掛起狀態(掛起態,suspend):暫時被調到外存等待的進程狀態稱為掛起狀態。掛起狀態由可以進一步細分為 就緒掛起、阻塞掛起 倆種狀態。
此時 進程的五狀態模型成為七狀態模型。如下圖所示:
在執行狀態的進程通過掛起即可進入就緒狀態,如圖所示,就緒狀態和阻塞狀態都分為活動態和靜止態。由活動態向靜止態轉換就是通過掛起實現的。
具有掛起狀態的轉換圖
在不少系統中進程只有上述五種狀態,但在另一些系統中,又增加了一些新狀態,最重要的是掛起狀態。引入掛起狀態的原因有:
(1) 終端用戶的請求。當終端用戶在自己的程序運行期間發現有可疑問題時,希望暫時使自己的程序靜止下來。亦即,使正在執行的進程暫停執行;若此時用戶進程正處於就緒狀態而未執行,則該進程暫不接受調度,以便用戶研究其執行情況或對程序進行修改。我們把這種靜止狀態稱為掛起狀態。
(2) 父進程請求。有時父進程希望掛起自己的某個子進程,以便考查和修改該子進程,或者協調各子進程間的活動。
(3) 負荷調節的需要。當實時系統中的工作負荷較重,已可能影響到對實時任務的控制時,可由系統把一些不重要的進程掛起,以保證系統能正常運行。
(4) 操作系統的需要。操作系統有時希望掛起某些進程,以便檢查運行中的資源使用情況或進行記賬。
創建狀態
創建一個進程一般要通過一下兩個兩個步驟
(1) 為一個新進程創建PCB,並填寫必要的管理信息.
(2) 把該進程轉入就緒狀態並插入就緒隊列之中。
當一個新進程被創建時,系統已為其分配了PCB,填寫了進程標識等信息,但由於該進程所必需的資源或其它信息,如主存資源尚未分配等,一般而言,此時的進程已擁有了自己PCB,但進程自身還未進入主存,即創建工作尚未完成,進程還不能被調度運行,其所處的狀態就是創建狀態。
引入創建狀態,是為了保證進程的調度必須在創建工作完成后進行,以確保對進程控制塊操作的完整性。同時,創建狀態的引入,也增加了管理的靈活性,操作系統可以根據系統性能或主存容量的限制,推遲創建狀態進程的提交。對於處於創建狀態的進程,獲得了其所必需的資源,以及對其PCB初始化工作完成后,進程狀態便可由創建狀態轉入就緒狀態。
終止狀態
等待操作系統進行善后處理,然后將其PCB清零,並將PCB空間返還系統。
當一個進程到達了自然結束點,或是出現了無法克服的錯誤,或是被操作系統所終結,或是被其他有終止權的進程所終結,它將進入終止狀態。
進入終止態的進程以后不能再執行,但在操作系統中依然保留一個記錄,其中保存狀態碼和一些計時統計數據,供其它進程收集。一旦其它進程完成了對終止狀態進程的信息提取之后,操作系統將刪除該進程
進程的調度算法:
1.先來先服務FCFS:該算法即可用於作業調度,也可用於進程調度
2.短作業優先/短進程優先 :SJF/ SPF :選擇剩余時間最短的
3.優先權優先 FPF
4.時間片輪詢法
線程的6種狀態及切換
Java中線程的狀態分為6種。
- 初始(NEW):新創建了一個線程對象,但還沒有調用start()方法。
- 運行(RUNNABLE):Java線程中將就緒(ready)和運行中(running)兩種狀態籠統的稱為“運行”。
線程對象創建后,其他線程(比如main線程)調用了該對象的start()方法。該狀態的線程位於可運行線程池中,等待被線程調度選中,獲取CPU的使用權,此時處於就緒狀態(ready)。就緒狀態的線程在獲得CPU時間片后變為運行中狀態(running)。- 阻塞(BLOCKED):表示線程阻塞於鎖。
- 等待(WAITING):進入該狀態的線程需要等待其他線程做出一些特定動作(通知或中斷)。
- 超時等待(TIMED_WAITING):該狀態不同於WAITING,它可以在指定的時間后自行返回。
- 終止(TERMINATED):表示該線程已經執行完畢。
這6種狀態定義在Thread類的State枚舉中,可查看源碼進行一一對應。
線程的狀態圖
狀態詳細說明
初始狀態(NEW)
實現Runnable接口和繼承Thread可以得到一個線程類,new一個實例出來,線程就進入了初始狀態。
就緒狀態(RUNNABLE之READY)
- 就緒狀態只是說你資格運行,調度程序沒有挑選到你,你就永遠是就緒狀態。
- 調用線程的start()方法,此線程進入就緒狀態。
- 當前線程sleep()方法結束,其他線程join()結束,等待用戶輸入完畢,某個線程拿到對象鎖,這些線程也將進入就緒狀態。
- 當前線程時間片用完了,調用當前線程的yield()方法,當前線程進入就緒狀態。
- 鎖池里的線程拿到對象鎖后,進入就緒狀態。
運行中狀態(RUNNABLE之RUNNING)
線程調度程序從可運行池中選擇一個線程作為當前線程時線程所處的狀態。這也是線程進入運行狀態的唯一的一種方式。
阻塞狀態(BLOCKED)
阻塞狀態是線程阻塞在進入synchronized關鍵字修飾的方法或代碼塊(獲取鎖)時的狀態。
等待(WAITING)
處於這種狀態的線程不會被分配CPU執行時間,它們要等待被顯式地喚醒,否則會處於無限期等待的狀態。
超時等待(TIMED_WAITING)
處於這種狀態的線程不會被分配CPU執行時間,不過無須無限期等待被其他線程顯示地喚醒,在達到一定時間后它們會自動喚醒。
終止狀態(TERMINATED)
-
當線程的run()方法完成時,或者主線程的main()方法完成時,我們就認為它終止了。這個線程對象也許是活的,但是它已經不是一個單獨執行的線程。線程一旦終止了,就不能復生。
-
在一個終止的線程上調用start()方法,會拋出java.lang.IllegalThreadStateException異常。
等待隊列
-
調用obj的wait(), notify()方法前,必須獲得obj鎖,也就是必須寫在synchronized(obj) 代碼段內。
-
與等待隊列相關的步驟和圖
- 線程1獲取對象A的鎖,正在使用對象A。
- 線程1調用對象A的wait()方法。
- 線程1釋放對象A的鎖,並馬上進入等待隊列。
- 鎖池里面的對象爭搶對象A的鎖。
- 線程5獲得對象A的鎖,進入synchronized塊,使用對象A。
- 線程5調用對象A的notifyAll()方法,喚醒所有線程,所有線程進入同步隊列。若線程5調用對象A的notify()方法,則喚醒一個線程,不知道會喚醒誰,被喚醒的那個線程進入同步隊列。
- notifyAll()方法所在synchronized結束,線程5釋放對象A的鎖。
- 同步隊列的線程爭搶對象鎖,但線程1什么時候能搶到就不知道了。
同步隊列狀態
- 當前線程想調用對象A的同步方法時,發現對象A的鎖被別的線程占有,此時當前線程進入同步隊列。簡言之,同步隊列里面放的都是想爭奪對象鎖的線程。
- 當一個線程1被另外一個線程2喚醒時,1線程進入同步隊列,去爭奪對象鎖。
- 同步隊列是在同步的環境下才有的概念,一個對象對應一個同步隊列。
- 線程等待時間到了或被notify/notifyAll喚醒后,會進入同步隊列競爭鎖,如果獲得鎖,進入RUNNABLE狀態,否則進入BLOCKED狀態等待獲取鎖。
幾個方法的比較
-
Thread.sleep(long millis),一定是當前線程調用此方法,當前線程進入
TIMED_WAITING
狀態,但不釋放對象鎖,millis后線程自動蘇醒進入就緒狀態。作用:給其它線程執行機會的最佳方式。 -
Thread.yield(),一定是當前線程調用此方法,當前線程放棄獲取的CPU時間片,但不釋放鎖資源,由運行狀態變為就緒狀態,讓OS再次選擇線程。作用:讓相同優先級的線程輪流執行,但並不保證一定會輪流執行。實際中無法保證yield()達到讓步目的,因為讓步的線程還有可能被線程調度程序再次選中。
Thread.yield()不會導致阻塞。該方法與sleep()類似,只是不能由用戶指定暫停多長時間。
-
thread.join()/thread.join(long millis),當前線程里調用其它線程t的join方法,當前線程進入WAITING/TIMED_WAITING狀態,當前線程不會釋放已經持有的對象鎖。線程t執行完畢或者millis時間到,當前線程一般情況下進入RUNNABLE狀態,也有可能進入BLOCKED狀態(因為join是基於wait實現的)。
-
obj.wait(),當前線程調用對象的wait()方法,當前線程釋放對象鎖,進入等待隊列。依靠notify()/notifyAll()喚醒或者wait(long timeout) timeout時間到自動喚醒
-
obj.notify()喚醒在此對象監視器上等待的單個線程,選擇是任意性的。notifyAll()喚醒在此對象監視器上等待的所有線程。
-
LockSupport.park()/LockSupport.parkNanos(long nanos),LockSupport.parkUntil(long deadlines), 當前線程進入WAITING/TIMED_WAITING狀態。
對比wait方法,不需要獲得鎖就可以讓線程進入WAITING/TIMED_WAITING狀態,需要通過LockSupport.unpark(Thread thread)喚醒。
注意點
等待隊列里許許多多的線程都wait()在一個對象上,此時某一線程調用了對象的notify()方法,那喚醒的到底是哪個線程?隨機?隊列FIFO?or sth else?
Java文檔就簡單的寫了句:選擇是任意性的(The choice is arbitrary and occurs at the discretion of the implementation)。