Java 線程和操做系統的線程有啥區別?

不想看解釋的小夥伴可直接翻到文末尋找答案。html

1. 用戶空間和內核空間git

關於內核態和用戶態咱們在 瞭解操做系統的那些事兒,從這篇文章開始 這篇文章中已經詳細介紹過,這裏再也不過多贅述。程序員

至於什麼是系統空間和用戶空間也很是好理解:在操做系統中,內存一般會被分紅用戶空間(User space)與內核空間(Kernel space)這兩個部分。當進程/線程運行在用戶空間時就處於用戶態,運行在內核空間時就處於內核態:web

  • 運行在內核態的程序能夠訪問用戶空間和內核空間,或者說它能夠訪問計算機的任何資源,不受限制,隨心所欲,例如協調 CPU 資源,分配內存資源,提供穩定的環境供應用程序運行等
  • 而應用程序基本都是運行在用戶態的,或者說用戶態就是提供應用程序運行的空間。運行在用戶態的程序只能訪問用戶空間

那爲何要區分用戶態和內核態呢安全

其實早期操做系統是不區分用戶態和內核態的,也就是說應用程序能夠訪問任意內存空間,若是程序不穩定經常會讓系統崩潰,好比清除了操做系統的內存數據。爲此大佬們設計出了一套規則:對於那些比較危險的操做須要切到內核態才能運行,好比 CPU、內存、設備等資源管理器程序就應該在內核態運行,不然安全性沒有保證。微信

舉個例子,對於文件系統和數據來講,文件系統數據和管理就必須放在內核態,可是用戶的數據和管理能夠放在用戶態。數據結構

用戶態的程序不能隨意操做內核地址空間,這樣有效地防止了操做系統程序受到應用程序的侵害多線程

那若是處於用戶態的程序想要訪問內核空間的話怎麼辦呢?就須要進行系統調用從用戶態切換到內核態。併發

2. 操做系統線程

① 在用戶空間中實現線程

早期的操做系統中,全部的線程都是在用戶空間下實現的,操做系統只能看到線程所屬的進程,而不能看到線程。app

從咱們開發者的角度來理解用戶級線程就是說:在這種模型下,咱們須要本身定義線程的數據結構、建立、銷燬、調度和維護等,這些線程運行在操做系統的某個進程內,而後操做系統直接對進程進行調度。

這種方式的好處一目瞭然,首先第一點,就是即便操做系統原生不支持線程,咱們也能夠經過庫函數來支持線程;第二點,線程的調度只發生在用戶態,避免了操做系統從內核態到用戶態的轉換開銷。

固然缺點也很明顯:因爲操做系統看不見線程,不知道線程的存在,而 CPU 的時間片切換是以進程爲維度的,因此若是進程中某個線程進行了耗時比較長的操做,那麼因爲用戶空間中沒有時鐘中斷機制,就會致使此進程中的其它線程由於得不到 CPU 資源而長時間的持續等待;另外,若是某個線程進行系統調用時好比缺頁中斷而致使了線程阻塞,此時操做系統也會阻塞住整個進程,即便這個進程中其它線程還在工做。

② 在內核空間中實現線程

所謂內核級線程就是運行在內核空間的線程, 直接由內核負責,只能由內核來完成線程的調度。

幾乎全部的現代操做系統,包括 Windows、Linux、Mac OS X 和 Solaris 等,都支持內核線程。

每一個內核線程能夠視爲內核的一個分身,這樣操做系統就有能力同時處理多件事情,支持多線程的內核就叫作多線程內核(Multi-Threads Kernel)。

從咱們開發者的角度來理解內核級線程就是說:咱們能夠直接使用操做系統中已經內置好的線程,線程的建立、銷燬、調度和維護等,都是直接由操做系統的內核來實現,咱們只須要使用系統調用就行了,不須要像用戶級線程那樣本身設計線程調度等。

上圖畫的是 1:1 的線程模型,所謂線程模型,也就是用戶線程和內核線程之間的關聯方式,線程模型固然不止 1:1 這一種,下面咱們來詳細解釋如下這三種多線程模型:

下文翻譯自 https://www.cs.uic.edu/~jbell/CourseNotes/OperatingSystems/4_Threads.html

1)多對一線程模型

  • 在多對一模型中,多個用戶級線程映射到某一個內核線程上
  • 線程管理由用戶空間中的線程庫處理,這很是有效
  • 可是,若是進行了阻塞系統調用,那麼即便其餘用戶線程可以繼續,整個進程也會阻塞
  • 因爲單個內核線程只能在單個 CPU 上運行,所以多對一模型不容許在多個 CPU 之間拆分單個進程

從併發性角度來總結下,雖然多對一模型容許開發人員建立任意多的用戶線程,可是因爲內核只能一次調度一個線程,因此並未增長併發性。如今已經幾乎沒有操做系統來使用這個模型了,由於它沒法利用多個處理核。

2)一對一線程模型

  • 一對一模型克服了多對一模型的問題
  • 一對一模型建立一個單獨的內核線程來處理每一個用戶線程
  • 可是,管理一對一模型的開銷更大,涉及更多開銷和減慢系統速度
  • 此模型的大多數實現都限制了能夠建立的線程數

從併發性角度來總結下,雖然一對一模型提供了更大的併發性,可是開發人員應注意不要在應用程序內建立太多線程(有時系統可能會限制建立線程的數量),由於管理一對一模型的開銷更大。Windows (從 Win95 開始) 和 Linux 都實現了線程的一對一模型

3)多對多線程模型

  • 多對多模型將任意數量的用戶線程複用到相同或更少數量的內核線程上,結合了一對一和多對一模型的最佳特性
  • 用戶對建立的線程數沒有限制
  • 阻止內核系統調用不會阻止整個進程
  • 進程能夠分佈在多個處理器上
  • 能夠爲各個進程分配可變數量的內核線程,具體取決於存在的 CPU 數量和其餘因素

3. Java 線程

在進入 Java 線程主題以前,有必要講解一下線程庫 Thread library 的概念。

在上面的模型介紹中,咱們提到了經過線程庫來建立、管理線程,那麼什麼是線程庫呢?

線程庫就是爲開發人員提供建立和管理線程的一套 API

固然,線程庫不只能夠在用戶空間中實現,還能夠在內核空間中實現。前者涉及僅在用戶空間內實現的 API 函數,沒有內核支持。後者涉及系統調用,也就是說調用庫中的一個 API 函數將會致使對內核的系統調用,而且須要具備線程庫支持的內核。

下面簡單介紹下三個主要的線程庫:

1)POSIX Pthreads:能夠做爲用戶或內核庫提供,做爲 POSIX 標準的擴展

2)Win32 線程:用於 Window 操做系統的內核級線程庫

3)Java 線程:Java 線程 API 一般採用宿主系統的線程庫來實現,也就是說在 Win 系統上,Java 線程 API 一般採用 Win API 來實現,在 UNIX 類系統上,採用 Pthread 來實現。

下面咱們來詳細講解 Java 線程:

事實上,在 JDK 1.2 以前,Java 線程是基於稱爲 "綠色線程"(Green Threads)的用戶級線程實現的,也就是說程序員大佬們爲 JVM 開發了本身的一套線程庫或者說線程管理機制。

在 JDK 1.2 及之後,JVM 選擇了更加穩定且方便使用的操做系統原生的內核級線程,經過系統調用,將線程的調度交給了操做系統內核。而對於不一樣的操做系統來講,它們自己的設計思路基本上是徹底不同的,所以它們各自對於線程的設計也存在種種差別,因此 JVM 中明確聲明瞭:虛擬機中的線程狀態,不反應任何操做系統中的線程狀態

也就是說,在 JDK 1.2 及以後的版本中,Java 的線程很大程度上依賴於操做系統採用什麼樣的線程模型,這點在不一樣的平臺上沒有辦法達成一致,JVM 規範中也並未限定 Java 線程須要使用哪一種線程模型來實現,多是一對一,也多是多對多或多對一。

總結來講,回答下文題,現今 Java 中線程的本質,其實就是操做系統中的線程,其線程庫和線程模型很大程度上依賴於操做系統(宿主系統)的具體實現,好比在 Windows 中 Java 就是基於 Wind32 線程庫來管理線程,且 Windows 採用的是一對一的線程模型

References

  • Operating Systems - Threads:https://www.cs.uic.edu/~jbell/CourseNotes/OperatingSystems/4_Threads.html
  • Java 線程和操做系統線程的關係:https://blog.csdn.net/CringKong/article/details/79994511?utm_medium



  • 博主小碩在讀,深耕 Java,目前在維護一個教程類倉庫 CS-Wiki 「Gitee 官方推薦項目,現已 1.5k+ star,倉庫地址:https://gitee.com/veal98/CS-Wiki」,公衆號上的文章也會在此同步更新,歡迎各位前來交流學習。
  • 準備春招秋招的小夥伴能夠參考個人這個論壇項目 Echo 「Gitee 官方推薦項目,現已 600+ star,倉庫地址:https://gitee.com/veal98/Echo」。配套教程正在同步更新中,公衆號後臺回覆 "Echo" 便可免費獲取。
  • 另外,雖然如今本號仍然很小,粉絲也沒多少,不過我仍是建了一個交流羣『 小牛肉和它的小夥伴們 』,感興趣的各位能夠下方掃碼加我微信回覆 "進羣",我拉你進羣:

本文分享自微信公衆號 - 飛天小牛肉(CS-Wiki)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索