Java多線程併發03——在Java中線程是如何調度的

在前兩篇文章中,我們已經瞭解了關於線程的創建與常用方法等相關知識。接下來就來了解下,當你運行線程時,線程是如何調度的。關注我的公衆號「Java面典」瞭解更多 Java 相關知識點。

多任務系統往往需要同時執行多道作業。作業數往往大於機器的 CPU 數,然而一顆 CPU 同時只能執行一項任務,如何讓用戶感覺這些任務正在同時進行呢? 操作系統的設計者 巧妙地利用了時間片輪轉的方式。

線程上下文

對於單核 CPU 來說(對於多核 CPU,此處就理解爲一個核),CPU 在一個時刻只能運行一個線程,當在運行一個線程的過程中轉去運行另外一個線程,這個叫做線程上下文切換(對於進程也是類似)。

上下文切換實現過程

  1. 掛起一個進程,將這個進程在 CPU 中的狀態(上下文)存儲於內存中的某處;
  2. 在內存中檢索下一個進程的上下文並將其在 CPU 的寄存器中恢復;
  3. 跳轉到程序計數器所指向的位置(即跳轉到進程被中斷時的代碼行),以恢復該進程在程序中。

引起線程上下文切換的原因

  1. 當前執行任務的時間片用完之後,系統 CPU 正常調度下一個任務;
  2. 當前執行任務碰到 IO 阻塞,調度器將此任務掛起,繼續下一任務;
  3. 多個任務搶佔鎖資源,當前任務沒有搶到鎖資源,被調度器掛起,繼續下一任務;
  4. 用戶代碼掛起當前任務,讓出 CPU 時間;
  5. 硬件中斷。

線程調度算法

操作系統中線程調度主要有兩種方式搶佔式調度協同式調度

搶佔式調度

搶佔式調度指的是每條線程執行的時間、線程的切換都由系統控制。系統控制指的是在系統某種運行機制下,可能每條線程都分同樣的執行時間片,也可能是某些線程執行的時間片較長,甚至某些線程得不到執行的時間片。在這種機制下,一個線程的堵塞不會導致整個進程堵塞。

協同式調度

協同式調度指某一線程執行完後主動通知系統切換到另一線程上執行。線程的執行時間由線程本身控制,線程切換可以預知,不存在多線程同步問題,但它有一個致命弱點:如果一個線程編寫有問題,運行到一半就一直堵塞,那麼可能導致整個系統崩潰。

JVM 的線程調度實現

Java 使用的線程調度使用搶佔式調度,Java 中線程會按優先級分配 CPU 時間片運行,且優先級越高越優先執行,但優先級高並不代表能獨自佔用執行時間片,可能是優先級高得到越多的執行時間片,反之,優先級低的分到的執行時間少但不會分配不到執行時間。

線程讓出 cpu 的情況:

  1. 當前運行線程主動放棄 CPU,JVM 暫時放棄 CPU 操作(基於時間片輪轉調度的 JVM 操作系統不會讓線程永久放棄 CPU,或者說放棄本次時間片的執行權),例如調用 yield() 方法;
  2. 當前運行線程因爲某些原因進入阻塞狀態,例如阻塞在 I/O 上;
  3. 當前運行線程結束,即運行完 run() 方法裏面的任務。

進程調度算法

操作系統中常用的進程調度算法,主要有三種優先調度算法高優先權先調度算法基於時間片的輪轉調度算法

優先調度算法

先來先服務調度算法(FCFS)

每次調度都是從後備作業隊列中選擇一個或多個最先進入該隊列的作業,將它們調入內存運行,分配執行資源。

短作業(進程)優先調度算法(SFJ)

每次都是從後備隊列中選擇一個或若干個估計運行時間最短的作業,將它們調入內存運行,分配執行資源。

採取優先調度算法時,線程一旦獲得 CPU 執行權限,將一直執行到完成,或發生某事件而被阻塞放棄處理機時再重新調度。優先調度算法的缺點是:未照顧到緊迫型作業。

高優先權優先調度算法

爲了照顧緊迫型作業,使之在進入系統後便獲得優先處理,引入了最高優先權優先(FPF)調度算法。當把該算法用於作業調度時,系統將從後備隊列中選擇若干個優先權最高的作業裝入內存。當用於進程調度時,該算法是把處理機分配給就緒隊列中優先權最高的進程。

非搶佔式優先權算法

特點:系統一旦把處理機分配給就緒隊列中優先權最高的進程後,該進程便一直執行下去,直至完成;或因發生某事件使該進程放棄處理機時。

適用系統:這種調度算法主要用於批處理系統中,也可用於某些對實時性要求不嚴的實時系統中。

搶佔式優先權調度算法

特點:當在系統執行期間,只要又出現了另一個其優先權更高的進程,進程調度程序就立即停止當前進程(原優先權最高的進程)的執行,重新將處理機分配給新到的優先權最高的進程。

適用系統:顯然,這種搶佔式的優先權調度算法能更好地滿足緊迫作業的要求,故而常用於要求比較嚴格的實時系統中,以及對性能要求較高的批處理和分時系統中。

高響應比優先調度算法

原理:在短作業優先算法的基礎上,爲每個作業引入優先權,並使作業的優先級隨着等待時間的增加而以速率 a 提高,則長作業在等待一定的時間後,必然有機會分配到處理機。

特點

  1. 如果作業的等待時間相同,則要求服務的時間愈短,其優先權愈高,因而該算法有利於短作業;
  2. 當要求服務的時間相同時,作業的優先權決定於其等待時間,等待時間愈長,其優先權愈高,因而它實現的是先來先服務;
  3. 對於長作業,作業的優先級可以隨等待時間的增加而提高,當其等待時間足夠長時,其優先級便可升到很高,從而也可獲得處理機。

優點:該算法既照顧了短作業,又考慮了作業到達的先後次序,不會使長作業長期得不到服務。

缺點:在利用該算法時,每要進行調度之前,都須先做響應比的計算,這會增加系統開銷。

基於時間片的輪轉調度算法

時間片輪轉法

實現

  1. 系統將所有的就緒進程按先來先服務的原則排成一個隊列,每次調度時,把 CPU 分配給隊首進程,並令其執行一個時間片;
  2. 當執行的時間片用完時,由一個計時器發出時鐘中斷請求,調度程序便據此信號來停止該進程的執行,並將它送往就緒隊列的末尾;
  3. 然後,再把處理機分配給就緒隊列中新的隊首進程,同時也讓它執行一個時間片。

這樣就可以保證就緒隊列中的所有進程在一給定的時間內均能獲得一時間片的處理機執行時間。

多級反饋隊列調度算法

實現

  1. 設置多個就緒隊列,併爲各個隊列賦予不同的優先級與不同的時間片。 第一個隊列的優先級最高,第二個隊列次之,其餘各隊列的優先權逐個降低。在優先權愈高的隊列中,爲每個進程所規定的執行時間片就愈小。例如,第二個隊列的時間片要比第一個隊列的時間片長一倍,……,第 i + 1 個隊列的時間片要比第 i 個隊列的時間片長一倍;
  2. 當一個新進程進入內存後,首先將它放入第一隊列的末尾,按 FCFS 原則排隊等待調度。當輪到該進程執行時,如它能在該時間片內完成,便可準備撤離系統;如果它在一個時間片結束時尚未完成,調度程序便將該進程轉入第二隊列的末尾,再同樣地按 FCFS 原則等待調度執行;如果它在第二隊列中運行一個時間片後仍未完成,再依次將它放入第三隊列,……,如此下去,當一個長作業(進程)從第一隊列依次降到第 n 隊列後,在第 n 隊列便採取按時間片輪轉的方式運行;
  3. 僅當第一隊列空閒時,調度程序才調度第二隊列中的進程運行。僅當第 1~(i-1)隊列均空時,纔會調度第 i 隊列中的進程運行。如果處理機正在第 i 隊列中爲某進程服務時,又有新進程進入優先權較高的隊列(第 1~(i-1)中的任何一個隊列),則此時新進程將搶佔正在運行進程的處理機,即由調度程序把正在運行的進程放回到第 i 隊列的末尾,把處理機分配給新到的高優先權進程。

在多級反饋隊列調度算法中,如果規定第一個隊列的時間片略大於多數人機交互所需之處理時間時,便能夠較好的滿足各種類型用戶的需要。

多線程與併發系列推薦

Java多線程併發02——線程的生命週期與常用方法,你都掌握了嗎

Java多線程併發01——線程的創建與終止,你會幾種方式

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章