Java 線程實現方式

進程與線程

在傳統的操做系統中,最核心的概念是「進程」,進程是對正在運行的程序的一個抽象。
進程的存在讓「並行」成爲了可能,在一個操做系統中,容許運行着多個進程,這些進程「看起來」是同時在運行的。
若是咱們的計算機同時運行着 web 瀏覽器、電子郵件客戶端、即時通信軟件例如QQ微信等多個進程,咱們感受這些進程都是同時在運行的,假設這臺計算機搭配的是多個 CPU 或者 多核 CPU,那麼這種多個進程並行的現象可能一點也不奇怪,徹底能夠爲每一個進程單獨分配一個 CPU,這樣就實現了多進程並行。
然而事實上,在計算機只有一個 CPU 的狀況下,它也能給人類一種感受:多個進程同時在運行。但人類的感受每每是比較模糊的,不精確的。事實是因爲 CPU 的計算速度很是地快,它能快速地在各個進程之間切換,在某一瞬間,CPU 只能運行一個進程,但一秒鐘以內,它就能經過快速切換,讓人產生多個進程同時在運行的錯覺。
在操做系統中,爲何在進程的基礎上,又衍生出了線程的概念呢?web

  1. 因爲對於一些進程而言,它內部會發生多種活動,有些活動可能會在某個時間裏阻塞,有些活動不會,若是經過線程將這些活動分離開使它們可以並行地運行,則設計程序的時候會更加簡單。
  2. 線程比進程的建立更加輕量級,性能消耗更少
  3. 若是一個進程既須要 CPU 計算,也須要I/O處理,擁有多線程容許這些活動重疊進行,加快整個進程的執行速度。

每個進程在操做系統中都擁有獨立的一塊內存地址空間,該進程建立的全部線程共享這塊內存,支持多線程的操做系統,會讓線程做爲 CPU 調度的最小單位。CPU 的時間片在不一樣的線程之間進行分配。編程

線程的可能實現方式

基本上主流的操做系統都支持線程,也提供了線程的實現。而 Java 語言爲了應對不一樣硬件和操做系統的差別,提供了對線程操做的統一抽象,在 Java 中咱們使用 Thread 類來表明一個線程。
Thread 的具體實現可能會有不一樣的實現方式:瀏覽器

使用內核線程實現

內核線程是操做系統內核支持的線程,在內核中有一個線程表用來記錄系統中的全部線程,建立或者銷燬一個線程時,都須要涉及到系統調用,而後再內核中對線程表進行更新操做。對內核線程的阻塞以及其它操做,都涉及到系統調用,系統調用的代價都比較大,涉及到在用戶態和內核態之間的來回切換。此外,內核內部有線程調度器,用於決定應該將 CPU 時間片分配個哪一個線程。
程序通常不會直接操做內核線程,而是使用內核線程的一種高級接口,輕量級進程。輕量級進程與內核線程之間的關係是 1:1,每個輕量級進程內部都有一個內核線程支持。微信

內核線程實現.png

上圖中, LWP 指 Light Weight Process,即輕量級進程;KLT 指 Kernel Level Thread,即內核線程。多線程

使用用戶線程實現

用戶線程是程序或者編程語言本身實現的線程庫,系統內核沒法感知到這些線程的存在。用戶線程的創建、同步、銷燬和調度,都在用戶態中完成,無須內核的幫助,不須要進行系統調用,這樣的好處是對於線程的操做是很是高效的。在這種狀況下,進程和用戶線程的比例是 1 :N。
用戶態線程實現.png編程語言

用戶態線程面對如何阻塞線程時,會面臨困難,阻塞一個用戶態線程會出現把整個進程都阻塞的狀況,多線程也就失去了意義。由於缺乏內核的支持,因此不少須要利用內核才能完成的工做,例如阻塞與喚醒線程、多 CPU 環境下線程的映射等,都須要用戶程序去實現,實現起來會異常困難。性能

使用用戶線程和內核線程混合實現

在這種混合實現下,既存在用戶線程,也存在內核線程。用戶態線程的建立、切換這些操做依然很高效,而且用戶態實現的線程,比較容易加大線程的規模。須要操做系統內核支持的功能,則經過內核線程來作到,例如映射到不一樣的處理器上、處理線程的阻塞與喚醒以及內核線程的調度等。這種實現依然會使用到輕量級進程 LWP,它是用戶線程和內核線程之間的橋樑。spa

混合實現.png

Java 線程的實現

在 JDK1.2 以前, Java 的線程是使用用戶線程實現的,在 JDK1.2 以後,Java 才採用操做系統原生支持的線程模型來實現,Java 線程模型的實現方式,主要取決於操做系統。對於 Oracle JDK 來講,在 Windows 和 Linux 上的線程模型是採用一對一的方式實現的,即一條 Java 線程映射到一條輕量級進程(內核線程)。而在 Solaris 平臺中,Java 則支持 1 :1 和 N : M 的線程模型。操作系統

線程的阻塞與等待

Java 中的線程的狀態有如下幾種:New,Runnable,Waiting, TimedWaiting, Blocked,Terminated。線程

線程狀態.png

  • NEW:線程初建立,未運行
  • RUNNABLE:線程正在運行,但不必定消耗 CPU
  • BLOCKED:線程正在等待另一個線程釋放鎖
  • WAITING:線程執行了 wait, join, LockSupport.park() 方法
  • TIMED_WAITING:線程調用了sleep, wait, join, LockSupport.parkNanos() 等方法,與 WAITING 狀態不一樣的是,這些方法帶有表示時間的參數。

其中 Blocked 和 Waiting 有個重要的區別是,阻塞(Blocked)狀態的線程在等待獲取一個排他鎖,例如線程在等待進入一個synchronized關鍵字包圍的臨界區時,就進入 Blocked 狀態。而 Waiting 狀態則是在等待被喚醒,或者等待一段時間。

參考資料

《深刻理解 Java 虛擬機》第二版 - 周志明
《現代操做系統》第四版 - Andrew S. Tanenbaum

掃一掃關注個人微信公衆號

相關文章
相關標籤/搜索