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

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

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

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

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

這里需要著重解釋一點(diǎn),在現(xiàn)在的操作系統(tǒng)中,因為線程依舊被視為輕量級進(jìn)程,所以操作系統(tǒng)中線程的狀態(tài)實(shí)際上和進(jìn)程狀態(tài)是一致的模型。
2.2.2 操作系統(tǒng)中線程和Java線程狀態(tài)的關(guān)系:
從實(shí)際意義上來講,操作系統(tǒng)中的線程除去new和terminated狀態(tài),一個線程真實(shí)存在的狀態(tài),只有:
ready:表示線程已經(jīng)被創(chuàng)建,正在等待系統(tǒng)調(diào)度分配CPU使用權(quán)。running:表示線程獲得了CPU使用權(quán),正在進(jìn)行運(yùn)算waiting:表示線程等待(或者說掛起),讓出CPU資源給其他線程使用
為什么除去new和terminated狀態(tài)?
是因為這兩種狀態(tài)實(shí)際上并不存在于線程運(yùn)行中,所以也沒什么實(shí)際討論的意義。
對于Java中的線程狀態(tài):
無論是Timed Waiting ,Waiting還是Blocked,對應(yīng)的都是操作系統(tǒng)線程的**waiting(等待**)狀態(tài)。
而Runnable狀態(tài),則對應(yīng)了操作系統(tǒng)中的ready和running狀態(tài)。
而對不同的操作系統(tǒng),由于本身設(shè)計思路不一樣,對于線程的設(shè)計也存在種種差異,所以JVM在設(shè)計上,就已經(jīng)聲明:
虛擬機(jī)中的線程狀態(tài),不反應(yīng)任何操作系統(tǒng)線程狀態(tài)
所以我上面說的那么多,只是作為理解模型,Java線程和操作系統(tǒng)線程,實(shí)際上同根同源,但又相差甚遠(yuǎn)。
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
一文吃透Spring?Cloud?gateway自定義錯誤處理Handler
這篇文章主要為大家介紹了一文吃透Spring?Cloud?gateway自定義錯誤處理Handler方法,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03
SpringBoot定時任務(wù)調(diào)度與爬蟲的配置實(shí)現(xiàn)
這篇文章主要介紹了SpringBoot定時任務(wù)調(diào)度與爬蟲的實(shí)現(xiàn),使用webmagic開發(fā)爬蟲,繼承PageProcessor接口編寫自己的處理類,process是定制爬蟲邏輯的核心接口,在這里編寫抽取邏輯,具體實(shí)現(xiàn)配置過程跟隨小編一起看看吧2022-01-01
Java中使用DOM4J生成xml文件并解析xml文件的操作
這篇文章主要介紹了Java中使用DOM4J來生成xml文件和解析xml文件的操作,今天通過代碼給大家展示了解析xml文件和生成xml文件的方法,需要的朋友可以參考下2021-09-09
Java處理圖片實(shí)現(xiàn)base64編碼轉(zhuǎn)換
這篇文章主要介紹了Java處理圖片實(shí)現(xiàn)base64編碼轉(zhuǎn)換,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-02-02
在實(shí)踐中了解Java反射機(jī)制應(yīng)用
當(dāng)程序運(yùn)行時,允許改變程序結(jié)構(gòu)或變量類型,這種語言稱為動態(tài)語言。我們認(rèn)為java并不是動態(tài)語言,但是它卻有一個非常突出的動態(tài)相關(guān)機(jī)制,俗稱:反射。下面我們來簡單學(xué)習(xí)一下吧2019-05-05
Java中動態(tài)地改變數(shù)組長度及數(shù)組轉(zhuǎn)Map的代碼實(shí)例分享
這篇文章主要介紹了Java中動態(tài)地改變數(shù)組長度及數(shù)組轉(zhuǎn)map的代碼分享,其中轉(zhuǎn)Map利用到了java.util.Map接口,需要的朋友可以參考下2016-03-03
初識sa-token及登錄授權(quán)簡單實(shí)現(xiàn)
這篇文章主要為大家介紹了sa-token及登錄授權(quán)簡單實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07

