面試官:都說阻塞 I/O 模型將會使線程休眠,為什麼 Java 線程狀態卻是 RUNNABLE?

来源:https://www.cnblogs.com/goodAndyxublog/archive/2019/09/30/11613770.html
-Advertisement-
Play Games

摘要: 原創出處 https://studyidea.cn 「公眾號:程式通事 」歡迎關註和轉載,保留摘要,謝謝! 使用 Java 阻塞 I/O 模型讀取數據,將會導致線程阻塞,線程將會進入休眠,從而讓出 CPU 的執行權,直到數據讀取完成。這個期間如果使用 jstack 查看線程狀態,卻可以發現J ...


摘要: 原創出處 https://studyidea.cn 「公眾號:程式通事 」歡迎關註和轉載,保留摘要,謝謝!

使用 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

  1. Object#wait

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

  1. Thread#join

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

  1. LockSupport#park()

LockSupport 是 JDK 併發包里重要對象,很多鎖的實現都依靠該對象。一旦調用 LockSupport#park(),線程就將會變為 WATTING 狀態。如果需要喚醒線程就需要調用 LockSupport#unpark,然後線程狀態重新變為 RUNNABLE

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

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

  1. Thread#sleep(long millis)
  2. 占有 synchronized 隱式鎖的線程調用 Object.wait (long timeout) 方法
  3. Thread#join (long millis)
  4. LockSupport#parkNanos (Object blocker, long deadline)
  5. LockSupport#parkUntil (long deadline)

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

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

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

java線程狀態2.png

通用操作系統線程狀態

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

操作系統線程狀態1.png

這 5 中狀態詳細情況如下:

  1. 初始狀態,這時候線程剛被創建,還不能分配 CPU 。
  2. 可運行狀態,線程等待系統分配 CPU ,從而執行任務。
  3. 運行狀態,操作系統將 CPU 分配給線程,線程執行任務。
  4. 休眠狀態,運行狀態下的線程如果調用阻塞 API,如阻塞方式讀取文件, 線程狀態就將變成休眠狀態。這種情況下,線程將會讓出 CPU 使用權。休眠結束,線程狀態將會先變成可運行狀態。
  5. 線程執行結束或者執行過程發生異常將會使線程進入終止狀態,這個狀態下線程使命已經結束。

對比兩者線程狀態

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

另外 Java 線程細分了操作系統休眠狀態,分成了 BLOCKEDWATTINGTIMED_WAITING 三種。

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

其他 Java 線程狀態與操作線程狀態類似。

面試官:都說阻塞 I/O 模型將會使線程休眠,為什麼 Java 線程狀態卻是 RUNNABLE?
其他平臺.png


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 摘要 作為一個web開發者,每天都在使用者Http協議,卻總是一知半解。本文參看Http RFC7230規範,梳理了http報文部分。 http 報文構成 start line: 起始行,描述請求或響應的基本信息 ( header field CRLF ): 頭 CRLF [ message bod ...
  • Higher Order Functions 定義的是方法,而不是函數 函數可作為變數存在,可直接調用,也可作為值傳遞給其他函數 尾碼將普通方法變為函數: 根據上下文編譯器可以自動將方法轉換為函數,也可省略 尾碼 高階函數,接收函數的函數 參數類型推導 匿名函數傳遞給其他函數或方法時,如果一直到參數 ...
  • 場景 當後臺項目由部署在一臺改為部署在多台以後,解決session共用問題最常用的辦法就是把session存儲在redis等緩存中。關於session和cookie概念這裡就不再贅述了,在springboot security環境下,把session存儲到redis中共用是非常非常簡單的,除了多了一 ...
  • 上完Java課,雖然也寫了不少的Java代碼,但是一直有不少的疑惑,而static關鍵字一直困惑著我很久,今天無意探究竟,上知乎再仔細查了一下,發現了這個話題的優秀答案https://www.zhihu.com/question/36615154 這篇文章徹底把我之前對static的理解概念土崩瓦解 ...
  • 異常 異常層次 Error:Java 運行時系統的內部錯誤和資源耗盡錯誤。應用程式不應該拋出這種類型的對象。如果出現了這樣的內部錯誤,除了通告給用戶,並儘力使程式安全地終止之外,再也無能為力了。 Exception RuntimeException:由程式錯誤導致的異常 其他異常:程式本身沒有問題, ...
  • 創建資料庫 前面介紹了springboot security整合jdbc從資料庫中查詢用戶的方式,適用性有限,下麵介紹最常用的整合MyBatis,這種在開發和生產環境中是最常用,也是最實用的。首先需要創建資料庫表,我們來創建三張表,分別是用戶表,角色表,還有用戶角色表,首先看用戶表: 只有三個欄位, ...
  • 有的時候我們會遇到這樣一個需求,一個用戶頁面中有多條履歷信息,每條履歷信息對應數據表中的一條記錄,用戶可以進行添加或修改,點擊保存時同時提交到了後臺。有兩個難點:1、前端怎樣一次性提交多條履歷信息?2、後臺如何獲取 form 表單提交的多個 name 屬性值相同的 input 標簽中的值? 例如: ...
  • 這道題是在與學弟吃飯的路上聽學弟講的,感覺挺有意思的,需要不少的思維(可能我長時間沒有刷題了,有點笨了~) 特此記錄一下: Problem: 有n個(x,y)元組,求從中取出k個元組,使得這k個元組的x之和乘以其中最小的y值的值最大 ( sum(x)*min(y) in k個元組 ) Solutio ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...