1. 程式人生 > >JVM學習之java執行緒實現&排程和狀態轉換

JVM學習之java執行緒實現&排程和狀態轉換

以下blog內容來自《深入理解Java虛擬機器_JVM高階特性與最佳實踐》感謝作者!

1 謹慎使用java 多執行緒  

如何提升效率:

     使用java時推薦利用多執行緒處理一些操作絕大多數情況下確實能提高效率,提高效率的原理在哪裡呢,為什麼說絕大多說情況呢。

       在CPU單核時代,我們知道某一時刻工作的執行緒只能是一條,那多執行緒為什麼還能提高效率呢,因為類似IO這種阻塞操作很多時候CPU需要等待檔案被從硬碟讀到記憶體之後才能幹活(IO密集型操作不適合多執行緒,因為io瓶頸無法避免,所以這裡只是舉個簡單例子說明情況),那就存在阻塞過程中CPU一直在等待,如果這個時候另開一個執行緒讓cpu去幹活就大大提高了效率。

      多CPU時代,同一時刻可以多個執行緒工作更能提升工作效率。

為什麼要謹慎使用:

多執行緒工作原理:cpu通過給每個執行緒分配cpu時間片,不停地切換執行緒的狀態來實現多執行緒(感覺同時執行是因為時間片較短),執行緒的建立和狀態切換是不容忽視的開銷。

當前任務執行一個時間片後會切換到下一個任務,在切換之前,上一個任務的狀態會被儲存下來,下次切換回這個任務時,可以再載入這個任務的狀態,任務從儲存到再載入的過程就是一次上下文切換。

考慮一種情況就是執行緒狀態切換的時間比執行緒工作時間還耗費時間或者相差不多時,此時多執行緒未必能提高效率。

2 執行緒實現方式

執行緒是比程序更輕量級的排程執行單位,執行緒把一個程序的資源分配和執行排程分開,各個執行緒既共享程序資源又獨立排程。

實現執行緒主要有三種方式:

使用核心執行緒實現,使用使用者執行緒實現和使用使用者執行緒加輕量級程序混合實現。

2.1 使用核心執行緒實現

核心執行緒就是直接由作業系統支援的執行緒,這種執行緒由核心來完成執行緒的切換,核心通過操縱排程器進行排程,並負責將執行緒的任務對映到各個處理器上。

程式一般不會直接去使用核心執行緒,而失去使用核心執行緒的一種高階介面-輕量級程序,每個輕量級程序都是由一個核心執行緒支援,因此只有先支援核心執行緒才能有輕量級程序。

輕量級程序的侷限:

(1)由於是基於核心執行緒實現的,所以各種執行緒操作,如建立,析構和同步都需要進行系統呼叫,系統呼叫需要在使用者態和核心態之間來回切換很消耗資源。

(2)每個輕量級程序都需要一個核心執行緒的支援,因此會消耗核心資源,所以一個系統支援的輕量級程序的數目是有限的。

2.2 使用使用者執行緒實現

狹義上來說使用者執行緒指的是完全建立在使用者空間的執行緒庫上構建執行緒框架,系統核心無法感知使用者執行緒存在的實現。

使用者執行緒的建立,同步,銷燬和排程完全在使用者態中完成,不需要核心的幫忙,甚至這種執行緒如果實現得當不需要切換到核心態,因此操作可以非常快速和高效,執行緒數目也可以 更大。

優勢劣勢:

系統使用者執行緒的優勢在於不需要系統核心支援,劣勢也在於沒有系統核心的支援,所有操作都需要使用者自己處理,可能實現起來更加複雜,此種方式在語言中較少使用。

2.3 使用使用者執行緒加輕量級程序混合實現

此種方式將核心執行緒與使用者執行緒一起使用,混合模式下既存在使用者執行緒又存在輕量級程序。使用者執行緒的建立,切換,析構等操作依然高效,利用輕量級程序作為使用者執行緒和核心執行緒之間的橋樑,可以使用核心提供的執行緒排程功能及處理器對映,並且使用者執行緒的系統呼叫要通過輕量級執行緒來完成,大大降低了整個程序被完全阻塞的風險。

2.4 java執行緒的實現方式

java Jdk1.2之前是基於稱為'綠色執行緒'的使用者執行緒實現的,Jdk1.2中執行緒模型替換成了基於作業系統原生執行緒模型來實現。

Sun JDK中,windows和Linux版都是使用一對一的執行緒模型實現,一條java執行緒對映到一條輕量級程序中,二Solaris平臺中可以同時支援一對一和多對多。

3 java 執行緒排程

執行緒排程指系統為執行緒分配處理器使用權的過程,主要排程方式有兩種,分別是協同式執行緒排程和搶佔式執行緒排程。

如果使用協同式排程的多執行緒系統,執行緒的執行時間由執行緒本身來控制,執行緒把自己的工作執行完了之後,要主動通知系統切換到另外一個執行緒上。

特點:

協同式多執行緒系統的優點是實現簡單,自己幹完活之後才切換執行緒,所以對切換現成的操作自己是知道的,沒有執行緒同步的問題。但存在的問題也很明顯,執行緒執行時間是不可控的,如果一個執行緒編寫的有問題可能會一直阻塞在那裡。

搶佔式呼叫的多執行緒系統每個執行緒將由系統來分配執行時間,執行緒的切換不由執行緒本身來決定,這種方式下現成的執行時間是系統可控的,不會有一個執行緒導致整個程序阻塞的問題,java使用的就是這種搶佔式排程。java執行緒還可以設定優先順序,但這種優先順序是不可靠的。

4 執行緒狀態的轉換

java語言定義了五種執行緒狀態,在任意一個時間點執行緒只能有且僅有其中的一種狀態。

五種狀態如下:

新建(New):建立後尚未啟動的執行緒處於這種狀態

執行(Runable):Runable包括了作業系統執行緒狀態中的Running和Ready,也就是處於此狀態的執行緒有可能正在執行,也有可能正在等著cpu分配時間片。

無限期等待(Waiting):處於這種狀態的執行緒不會被分配cpu時間,它們要等待被其他執行緒顯式的喚醒。

類似沒有設定Timeout引數的Object.wait(),沒有設定Timeout引數的Thread.join()方法

限期等待(Timed Waiting):處於這種狀態的執行緒也不會被分配cpu執行時間,不過無須等待被其他執行緒顯式的喚醒,一定時間之後會由系統自動喚醒。

阻塞(Blocked):執行緒被阻塞了,阻塞與等待狀態的區別是,阻塞狀態在等待著獲取到一個排它鎖,這個事件將在另外一個執行緒放棄這個鎖時發生,而等待狀態則是在等待一段時間或者喚醒動作的發生,在程式等待進入同步區域的時候,執行緒將進入這種狀態。

結束(Terminated):已終止執行緒的執行緒狀態,執行緒已經結束執行。