1. 程式人生 > >多線程實現原理

多線程實現原理

threads 共享 又是 進行 數據結構 線程模型 alt 進程 通信

相關閱讀

徹底搞懂 CPU 中的內存結構

Java 內存模型 ,一篇就夠了!

首先,多線程的出現是為了加快處理任務的效率,結合之前說過的底層 CPU 的介紹我們可以知道,在操作系統層面上,線程是操作系統任務調度的最小單位,進程是資源分配的最小單位,一個進程可以包含多個線程,線程共享進程中的資源。

說個形象的比喻,進程就像是一個巨大的工廠在作業,而線程就是工廠中的一個個的生產線。我下文中常說的任務,指的是和線程對應的一個個的生產線。

類比到我們的 Java 項目中來,可能有些任務我們可以抽離出來,獨立執行,而這就可以引入線程的概念,又因為萬物皆是對象,於是 Java 中就定義了 Thread 對象來表示線程。

但是前面說過了,線程的出現是為了執行任務的,那 Java 中又怎麽表示具體的任務的呢?Java 中提供了 Runnable 和 Callable 接口,具體的任務邏輯自己實現。

說到這你有沒有感覺,我們常說的 Java 中實現多線程的方式有點奇怪,說實在的 Java 中線程的實現就一個啊,就是繼承 Thread 類,其它的都是與線程綁定的任務呢。但是,理解就行,面試的時候慎重說只有一種。

在 Java 語言層面,我們是很輕松,只需繼承 Thread 類,調用 start 方法就算搞定,但是在 JVM 和操作系統層面又是如何實現的呢?

JVM 實現多線程使用的編程語言是 C++ ,具體的實現還是依賴與不同的操作系統,更多的工作還是在處理線程安全,這個我們上次已經說過了,在 JMM 中的設計中可以看出。

重頭戲來了,操作系統如何實現多線程,主要有 3 種方式,使用內核線程實現,使用用戶線程實現和使用用戶線程加輕量級進程混合實現。

使用內核線程實現

內核線程(KLT,Kernel-Level Thread),直接由操作系統內核(Kernel,即內核)支持的線程。由內核來完成線程切換,內核通過操縱調度器(Scheduler)對線程進行調度,並負責將線程的任務映射到各個處理器上。每個內核線程可以視為內核的一個分身,這樣操作系統就有能力同時處理多件事情,支持多線程的內核叫做多線程內核。

程序一般不會去直接使用內核線程,而是去使用內核線程的一種高級接口——輕量級進程(LWP),即通常意義上的線程。由於每個輕量級進程都由一個內核線程支持,因此只有先支持內核線程,才能有輕量級進程。輕量級進程與內核線程之間 1:1 關系稱為一對一的線程模型。

技術分享圖片

優點:在多處理器系統,內核能夠同時調度同一進程中的多個線程並發執行;如果一個進程的某個線程被阻塞,內核可以調度該進程的其他線程運行,也可以調度其他進程的線程運行;線程的切換比較快,切換開銷小;內核本身也可以采用多線程技術,可以提高系統的執行速度和效率;

缺點:對於用戶線程的調度,其模式切換開銷比較大,在同一個進程中,從一個線程切換到另一個線程時,需要從用戶態轉到核心態實現,這是因為,線程運行在用戶態,而線程調度和管理是在內核實現的,因此系統開銷較大;

使用用戶線程實現

用戶線程(User Thread)是在用戶空間實現的,對線程的創建、撤銷、通信、同步等功能的實現,無需內核的支持,因而內核完全不知道用戶級線程的存在;此時進程和用戶線程是一對多的關系。

線程切換不需要轉到內核空間。對一個進程而言,其所有的線程管理數據結構均在該進程自己的用戶空間,管理線程切換的線程庫也在用戶地址空間運行,從而不需要切換到內核方式來管理線程,從而節省了內核態和用戶態之間切換的開銷;

又因為對線程的創建、撤銷、通信、同步等功能的實現不需要內核來完成,都需要用戶程序自己來處理,用戶程序一般都比較復雜,所以,用這種方式來處理創建多線程的程序越來越少。

技術分享圖片

使用用戶線程加輕量級進程混合實現

在這種模式下,即存在用戶線程,也存在輕量級進程。用戶線程還是完全建立在用戶空間中,因此用戶線程的創建、切換等操作依然廉價,並且可以支持大規模的用戶線程並發。

而操作系統提供支持的輕量級進程則作為用戶線程和內核線程之間的橋梁,這樣可以使用內核提供的線程調度功能及時處理映射,並且用戶線程的系統要通過輕量級進程來完成,大大降低了整個進程被完全阻塞的風險。在這種混合模式中,用戶線程和輕量級進程的數量比是不確定的,即為 N:M 的關系。

技術分享圖片

上面 3 種創建進程的方式是不同的操作系統可能會采用的,而 Java 中具體采用哪一種要看 JVM 的具體實現。

Java 線程在 JDK 1.2 之前,是基於稱為“綠色線程”(Green Threads)的用戶線程實現的,而在 JDK 1.2 中,線程模型替換為基於操作系統原生線程模型來實現。

因此,在目前的 JDK 版本中,操作系統支持怎樣的線程模型,在很大程度上決定了 Java 虛擬機的線程是怎樣映射的,這點在不同的平臺上沒有辦法達成一致,虛擬機規範中也並未限定 Java 線程需要使用哪種線程模型來實現。

對於 Sun JDK 來說,它的 Windows 版與 Linux 版都是使用一對一的線程模型實現的,一條 Java 線程就映射到一條輕量級進程之中,因為 Windows 和 Linux 系統提供的線程模型就是一對一的。

而在 Solaris 平臺中,由於操作系統的線程特性可以同時支持一對一及多對多的線程模型,因此在 Solaris 版的 JDK 中也對應提供了兩個平臺專有的虛擬機參數:-XX:+UseLWPSynchronization(默認值)和-XX:+UseBoundThreads 來明確指定虛擬機使用哪種線程模型。

多線程實現原理