1. 程式人生 > 其它 >在阻塞式io中,如果一個執行緒在等待io操作,那麼cpu還會分配時間片給該執行緒嗎?

在阻塞式io中,如果一個執行緒在等待io操作,那麼cpu還會分配時間片給該執行緒嗎?

在阻塞式io中,如果一個執行緒在等待io操作,那麼cpu還會分配時間片給該執行緒嗎?{執行態,就緒態,阻塞態}

執行態---wait/阻塞io-→阻塞態

執行態-------排程--------→就緒態

就緒態-------排程--------→執行態

阻塞態---訊號/io返回-→就緒態

所以不佔用時間片。

既然阻塞 I/O 會使執行緒休眠,為什麼 Java 執行緒狀態卻是 RUNNABLE?

使用 Java 阻塞 I/O 模型讀取資料,將會導致執行緒阻塞,執行緒將會進入休眠,從而讓出 CPU 的執行權,直到資料讀取完成。這個期間如果使用 jstack 檢視執行緒狀態,卻可以發現Java 執行緒狀態是處於 RUNNABLE,這就和上面說的存在矛盾,為什麼會這樣?

上面的矛盾其實是混淆了作業系統執行緒狀態與 Java 執行緒狀態。這裡說的執行緒阻塞進入休眠狀態,其實是作業系統層面執行緒實際狀態。而我們使用 jstack 檢視的執行緒狀態卻是 JVM 中的執行緒狀態。

執行緒是作業系統中一種概念,Java 對其進行了封裝,Java 執行緒本質上就是作業系統的中執行緒,其狀態與作業系統的狀態大致相同,但還是存在一些區別。

下面首先來看我們熟悉的 Java 執行緒狀態。

Java 執行緒狀態
Java 執行緒狀態定義在 Thread.State 列舉中,使用 thread#getState 方法可以獲取當前執行緒的狀態。

Thread.State 狀態如下圖:

State.png
可以看到 Java 執行緒總共存在 6 中狀態,分別為:

NEW(初始狀態)

RUNNABLE(執行狀態)

BLOCKED(阻塞狀態)

WATTING(等待狀態)

TIMED_WAITING(限時等待狀態)

TERMINATED(終止狀態)

NEW(初始狀態)與 RUNNABLE(執行狀態)

每個使用 new Thread() 剛創建出執行緒例項狀態處於 NEW 狀態,一旦呼叫 thread.start(),執行緒狀態將會變成 RUNNABLE。

RUNNABLE(執行狀態) 與 BLOCKED(阻塞狀態)

RUNNABLE 狀態的執行緒在進入由 synchronized修飾的方法或程式碼塊前將會嘗試獲取一把隱式的排他鎖,一旦獲取不到,執行緒狀態將會變成 BLOCKED,等待獲取鎖。一旦有其他執行緒釋放這把鎖,執行緒成功搶到該鎖,執行緒狀態就將會從 BLOCKED 轉變為 RUNNABLE 狀態。

RUNNABLE(執行狀態) 與 WATTING(等待狀態)

處於 WATTING 狀態的執行緒將會一直處於無限期的等待狀態,需要等待其他執行緒喚醒。總共存在三種方法將會使執行緒從 RUNNABLE 變成 WATTING。

Object#wait

執行緒在獲取到 synchronized 隱式鎖後,顯示的呼叫 Object#wait()方法。這種情況下該執行緒將會讓出隱式鎖,一旦其他執行緒獲取到該鎖,且呼叫了 Object.notify() 或object.notifyAll(),執行緒將會喚醒,然後變成 RUNNABLE。

Thread#join

join方法是一種執行緒同步方法。假設我們在 main 方法中執行 Thread A.join() 方法,main 執行緒狀態就會變成 WATTING。直到 A 執行緒執行完畢,main 執行緒才會再變成 RUNNABLE。

LockSupport#park()

LockSupport 是 JDK 併發包裡重要物件,很多鎖的實現都依靠該物件。一旦呼叫 LockSupport#park(),執行緒就將會變為 WATTING 狀態。如果需要喚醒執行緒就需要呼叫 LockSupport#unpark,然後執行緒狀態重新變為 RUNNABLE。

RUNNABLE(執行狀態) 與 TIMED_WAITING(限時等待狀態)

TIMED_WAITING 與 WATTING 功能一樣,只不過前者增加限時等待的功能,一旦等待時間超時,執行緒狀態自動變為 RUNNABLE。以下幾種情況將會觸發這種狀態:

Thread#sleep(long millis)

佔有 synchronized 隱式鎖的執行緒呼叫 Object.wait (long timeout) 方法

Thread#join (long millis)

LockSupport#parkNanos (Object blocker, long deadline)

LockSupport#parkUntil (long deadline)

RUNNABLE(執行狀態)與 TERMINATED(終止狀態)

執行緒一旦執行結束或者執行緒執行過程發生異常且未正常捕獲處理,狀態都將會自動變成 TERMINATED。

Java 執行緒 6 種狀態看起來挺複雜的,但其實上面 BLOCKED,WATTING,TIMED_WAITING,都會使執行緒處於休眠狀態,所以我們將這三類都歸類為休眠狀態。這麼分類的話,Java 執行緒生命週期就可以簡化為下圖:

java執行緒狀態2.png

通用作業系統執行緒狀態

上面講完 Java 系統的執行緒狀態,我們來看下通用作業系統的執行緒狀態。作業系統執行緒狀態可以分為初始狀態,可執行狀態,執行狀態,休眠狀態以及終止狀態,如下圖:

作業系統執行緒狀態1.png
這 5 中狀態詳細情況如下:

初始狀態,這時候執行緒剛被建立,還不能分配 CPU 。

可執行狀態,執行緒等待系統分配 CPU ,從而執行任務。

執行狀態,作業系統將 CPU 分配給執行緒,執行緒執行任務。

休眠狀態,執行狀態下的執行緒如果呼叫阻塞 API,如阻塞方式讀取檔案, 執行緒狀態就將變成休眠狀態。這種情況下,執行緒將會讓出 CPU 使用權。休眠結束,執行緒狀態將會先變成可執行狀態。

執行緒執行結束或者執行過程發生異常將會使執行緒進入終止狀態,這個狀態下執行緒使命已經結束。

對比兩者執行緒狀態
比較 Java 執行緒與作業系統執行緒,可以發現 Java 執行緒狀態沒有可執行狀態。也就是說 Java 執行緒 RUNNABLE 狀態包括了作業系統的可執行狀態與執行狀態。一個處於 RUNNABLE 狀態 Java 執行緒,在作業系統層面狀態可能為可執行狀態,正在等待系統分配 CPU 使用權。

另外 Java 執行緒細分了作業系統休眠狀態,分成了 BLOCKED,WATTING,TIMED_WAITING 三種。

當執行緒呼叫阻塞式 API,執行緒進入休眠狀態,這裡指的是作業系統層面的。從 JVM 層面,Java 執行緒狀態依然處於 RUNNABLE 狀態。JVM 並不關心作業系統執行緒實際狀態。從 JVM 看來等待 CPU 使用權(作業系統執行緒狀態為可執行狀態)與等待 I/O (作業系統執行緒狀態處於休眠狀態)沒有區別,都是在等待某種資源,所以都歸入 RUNNABLE 狀態。

其他 Java 執行緒狀態與操作執行緒狀態類似。

說說sleep和wait的區別以及執行緒的狀態分析

執行緒的狀態分為

1,可執行(就緒):執行緒被建立之後,呼叫Start()函式就到了這個狀態。

2,執行:Start()函式之後,CPU切換到了這個執行緒開始執行裡面的Run方法就稱為執行狀態。

3,阻塞:阻塞狀態是指執行緒因為某種原因放棄了cpu執行權,暫時停止執行。直到執行緒進入可執行(runnable)狀態,才有機會再次獲得cpu 執行權 轉到執行(running)狀態。阻塞的情況分三種。

(一). 等待阻塞:執行(running)的執行緒執行o.wait()方法,JVM會把該執行緒放入等待佇列(waitting queue)中。

(二). 同步阻塞:執行(running)的執行緒在獲取物件的同步鎖時,若該同步鎖被別的執行緒佔用,則JVM會把該執行緒放入鎖池(lock pool)中。

(三). 其他阻塞:執行(running)的執行緒執行Thread.sleep(long ms)或t.join()方法,或者發出了I/O請求時,JVM會把該執行緒置為阻塞狀態。當sleep()狀態超時、join()等待執行緒終止或者超時、或者I/O處理完畢時,執行緒重新轉入可執行(runnable)狀態。

4,結束

執行緒run()、main() 方法執行結束,或者因異常退出了run()方法,則該執行緒結束生命週期。死亡的執行緒不可再次復生。

貼一張圖來分析5個狀態之間的關係

通過這張流程圖我們已經分析的七七八八了,然後我再總結一下sleep與wait的區別。

sleep()和wait()這兩個函式被呼叫之後執行緒都應該放棄執行權,不同的是sleep()不釋放鎖而wait()的話是釋放鎖。直白的意思是一個執行緒呼叫Sleep()之後進入了阻塞狀態中的其他阻塞,它的意思就是當sleep()狀態超時、join()等待執行緒終止或者超時,執行緒重新轉入可執行(runnable)狀態。而Wait()是不同的在釋放執行權之後wait也把鎖釋放了進入了執行緒等待阻塞,它要執行的話還是要和其他的執行緒去競爭鎖,之後才可以獲得執行權。

參考:https://www.jianshu.com/p/c655e0a944ae 參考:https://blog.csdn.net/javageektech/article/details/101443127 參考:https://www.zhihu.com/question/27734728