Java線程和操作系統(tǒng)線程的關(guān)系解讀
1.操作系統(tǒng)線程模型
1.1 線程實現(xiàn)在用戶空間下
當線程在用戶空間下實現(xiàn)時,操作系統(tǒng)對線程的存在一無所知,操作系統(tǒng)只能看到進程,而不能看到線程。所有的線程都是在用戶空間實現(xiàn)。
在操作系統(tǒng)看來,每一個進程只有一個線程。過去的操作系統(tǒng)大部分是這種實現(xiàn)方式,這種方式的好處之一就是即使操作系統(tǒng)不支持線程,也可以通過庫函數(shù)來支持線程。
我們換一種通俗的方式來講解這段話,首先就是在這在模型下,程序員需要自己實現(xiàn)線程的數(shù)據(jù)結(jié)構(gòu)、創(chuàng)建銷毀和調(diào)度維護。也就相當于需要實現(xiàn)一個自己的線程調(diào)度內(nèi)核,而同時這些線程運行在操作系統(tǒng)的一個進程內(nèi),最后操作系統(tǒng)直接對進程進行調(diào)度。

這樣做有一些優(yōu)點,首先就是確實在操作系統(tǒng)中實現(xiàn)了真實的多線程,其次就是線程的調(diào)度只是在用戶態(tài),減少了操作系統(tǒng)從內(nèi)核態(tài)到用戶態(tài)的切換開銷。
當然缺點也很明顯:
這種模式最致命的缺點也是由于操作系統(tǒng)不知道線程的存在,因此當一個進程中的某一個線程進行系統(tǒng)調(diào)用時,比如缺頁中斷而導致線程阻塞,此時操作系統(tǒng)會阻塞整個進程,即使這個進程中其它線程還在工作。
還有一個問題是假如進程中一個線程長時間不釋放CPU,因為用戶空間并沒有時鐘中斷機制,會導致此進程中的其它線程得不到CPU而持續(xù)等待。
1.2 線程實現(xiàn)在操作系統(tǒng)內(nèi)核中
內(nèi)核線程就是直接由操作系統(tǒng)內(nèi)核(Kernel)支持的線程,這種線程由內(nèi)核來完成線程切換,內(nèi)核通過操縱調(diào)度器(Scheduler)對線程進行調(diào)度,并負責將線程的任務映射到各個處理器上。
每個內(nèi)核線程可以視為內(nèi)核的一個分身,這樣操作系統(tǒng)就有能力同時處理多件事情,支持多線程的內(nèi)核就叫做多線程內(nèi)核(Multi-Threads Kernel)。
通俗的將就是,程序員直接使用操作系統(tǒng)中已經(jīng)實現(xiàn)的線程,而線程的創(chuàng)建、銷毀、調(diào)度和維護,都是靠操作系統(tǒng)(準確的說是內(nèi)核)來實現(xiàn),程序員只需要使用系統(tǒng)調(diào)用,而不需要自己設計線程的調(diào)度算法和線程對CPU資源的搶占使用。
而不得提到的就是,Linux下的輕量級進程,因為Linux并不像Windows那樣,真正的實現(xiàn)了線程的數(shù)據(jù)結(jié)構(gòu),而是直接采用了和系統(tǒng)中進程一樣的實現(xiàn)方式。
目前的Linux已經(jīng)基于NPTL實現(xiàn)了更符合POSIX標準的線程,之前我學習到的LinuxThreads早已經(jīng)被替代,這里為自己的不求甚解反省。
輕量級進程(LWP)是建立在內(nèi)核之上并由內(nèi)核支持的用戶線程,它是內(nèi)核線程的高度抽象,每一個輕量級進程都與一個特定的內(nèi)核線程關(guān)聯(lián)。內(nèi)核線程只能由內(nèi)核管理并像普通進程一樣被調(diào)度。

Linux中程序一般不會直接去使用內(nèi)核線程,而是去使用內(nèi)核線程的一種高級接口–輕量級進程,輕量級進程就是我們通常意義上所講的線程,由于每個輕量級進程都由一個內(nèi)核線程支持,因此只有先支持內(nèi)核線程,才能有輕量級進程。 這種輕量級進程與內(nèi)核線程之間1:1的關(guān)系稱為一對一的線程模型。
這里要說明的是,一對一的線程模型是一種概念,Linux是一對一的線程模型,上面的表述太繞了,現(xiàn)在的Linux中線程已經(jīng)的被更優(yōu)雅的實現(xiàn)了。
PS:其實Linux中的pthread庫就是調(diào)用了輕量級線程接口,來在操作系統(tǒng)中創(chuàng)建一個內(nèi)核線程。
Pthread庫目前也是基于NPTL實現(xiàn),已經(jīng)是一種符合POSIX標準的線程模型了,Linux擺脫了原來LWP的陰影。
1.3使用用戶線程加輕量級進程混合實現(xiàn)
在這種混合實現(xiàn)下,即存在用戶線程,也存在輕量級進程。用戶線程還是完全建立在用戶空間中,因此用戶線程的創(chuàng)建、切換、析構(gòu)等操作依然廉價,并且可以支持大規(guī)模的用戶線程并發(fā)。
而操作系統(tǒng)提供支持的輕量級進程則作為用戶線程和內(nèi)核線程之間的橋梁,這樣可以使用內(nèi)核提供的線程調(diào)度功能及處理器映射,并且用戶線程的系統(tǒng)調(diào)用要通過輕量級進程來完成,大大降低了整個進程被完全阻塞的風險。
在這種混合模式中,用戶線程與輕量級進程的數(shù)量比是不定的,即為N:M的關(guān)系:

明白了前面兩種模型,就應該很好理解這種線程模型了,但實際上現(xiàn)在主流的操作系統(tǒng)已經(jīng)不太常用這種線程模型了。
2019.3.26更新:
目前來說,作為異步回調(diào)以外的另一種解決方案,這種m:n的線程模型可以說大有可為,Golang的協(xié)程就是使用了這種模型,在用戶態(tài),協(xié)程能快速的切換,避免了線程調(diào)度的CPU開銷問題,協(xié)程相當于線程的線程。
2.Java線程
2.1 Java線程在操作系統(tǒng)上本質(zhì)
Java線程在JDK1.2之前,是基于稱為“綠色線程”(Green Threads)的用戶線程實現(xiàn)的,而在JDK1.2中,線程模型替換為基于操作系統(tǒng)原生線程模型來實現(xiàn)。
因此,在目前的JDK版本中,操作系統(tǒng)支持怎樣的線程模型,在很大程度上決定了Java虛擬機的線程是怎樣映射的,這點在不同的平臺上沒有辦法達成一致,虛擬機規(guī)范中也并未限定Java線程需要使用哪種線程模型來實現(xiàn)。
線程模型只對線程的并發(fā)規(guī)模和操作成本產(chǎn)生影響,對Java程序的編碼和運行過程來說,這些差異都是透明的。
也就說JDK1.2之前,
**程序員們?yōu)镴VM開發(fā)了自己的一個線程調(diào)度內(nèi)核,而到操作系統(tǒng)層面就是用戶空間內(nèi)的線程實現(xiàn)。
**而到了JDK1.2及以后,JVM選擇了更加穩(wěn)健且方便使用的操作系統(tǒng)原生的線程模型,通過系統(tǒng)調(diào)用,將程序的線程交給了操作系統(tǒng)內(nèi)核進行調(diào)度
對于Sun JDK來說,它的Windows版與Linux版都是使用一對一的線程模型實現(xiàn)的,一條Java線程就映射到一條輕量級進程之中,因為Windows和Linux系統(tǒng)提供的線程模型就是一對一的。
也就是說,現(xiàn)在的Java中線程的本質(zhì),其實就是操作系統(tǒng)中的線程,Linux下是基于pthread庫實現(xiàn)的輕量級進程,Windows下是原生的系統(tǒng)Win32 API提供系統(tǒng)調(diào)用從而實現(xiàn)多線程。
2.2 Java中的線程

特別注意:這些線程的狀態(tài)時JVM中的線程狀態(tài)!不是操作系統(tǒng)中的線程狀態(tài)。
2.2.1 操作系統(tǒng)中的進程(線程)狀態(tài)**(區(qū)分和JVM中的線程狀態(tài))**

這里需要著重解釋一點,在現(xiàn)在的操作系統(tǒng)中,因為線程依舊被視為輕量級進程,所以操作系統(tǒng)中線程的狀態(tài)實際上和進程狀態(tài)是一致的模型。
2.2.2 操作系統(tǒng)中線程和Java線程狀態(tài)的關(guān)系:
從實際意義上來講,操作系統(tǒng)中的線程除去new和terminated狀態(tài),一個線程真實存在的狀態(tài),只有:
ready:表示線程已經(jīng)被創(chuàng)建,正在等待系統(tǒng)調(diào)度分配CPU使用權(quán)。running:表示線程獲得了CPU使用權(quán),正在進行運算waiting:表示線程等待(或者說掛起),讓出CPU資源給其他線程使用
為什么除去new和terminated狀態(tài)?
是因為這兩種狀態(tài)實際上并不存在于線程運行中,所以也沒什么實際討論的意義。
對于Java中的線程狀態(tài):
無論是Timed Waiting ,Waiting還是Blocked,對應的都是操作系統(tǒng)線程的**waiting(等待**)狀態(tài)。
而Runnable狀態(tài),則對應了操作系統(tǒng)中的ready和running狀態(tài)。
而對不同的操作系統(tǒng),由于本身設計思路不一樣,對于線程的設計也存在種種差異,所以JVM在設計上,就已經(jīng)聲明:
虛擬機中的線程狀態(tài),不反應任何操作系統(tǒng)線程狀態(tài)
所以我上面說的那么多,只是作為理解模型,Java線程和操作系統(tǒng)線程,實際上同根同源,但又相差甚遠。
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java將List轉(zhuǎn)換為String的幾種常見方式
在實際開發(fā)中經(jīng)常遇到List轉(zhuǎn)為String字符串的情況,下面這篇文章主要給大家介紹了關(guān)于Java將List轉(zhuǎn)換為String的幾種常見方式,文中通過代碼介紹的非常詳細,需要的朋友可以參考下2024-03-03
jstack報錯Unable to open socket file解決
這篇文章主要為大家介紹了jstack報錯Unable to open socket file的解決方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2024-02-02
Apache?Commons?CLI構(gòu)建命令行應用利器教程
這篇文章主要為大家介紹了構(gòu)建命令行應用利器Apache?Commons?CLI的使用教程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-12-12
Java利用Spire.PDF實現(xiàn)將PDF文檔轉(zhuǎn)換為Word格式
在日常工作和學習中,我們經(jīng)常會遇到PDF文檔,本文將詳細介紹如何才能高效、精準地使用Java實現(xiàn)PDF到Word的自動化轉(zhuǎn)換,感興趣的小伙伴可以了解下2025-09-09

