「多線程系列 通俗易懂」并發(fā)編程的理論基石

一、進(jìn)程和線程

1.操作系統(tǒng)、進(jìn)程、線程的關(guān)系

操作系統(tǒng)是包含多個(gè)進(jìn)程的容器,而每個(gè)進(jìn)程又是容納多個(gè)線程的容器。


2.Oracle 官方定義

官方定義

進(jìn)程:使用 fork(2) 系統(tǒng)調(diào)用創(chuàng)建的UNIX 環(huán)境(例如文件描述符,用戶 ID 等),它被設(shè)置為運(yùn)行程序。

線程:在進(jìn)程上下文執(zhí)行的一系列指令。

3.什么是進(jìn)程

進(jìn)程(Process)是程序的運(yùn)行實(shí)例。

進(jìn)程是程序向操作系統(tǒng)申請(qǐng)資源(如內(nèi)存空間和文件句柄)的基本單位。

在用戶下達(dá)運(yùn)行程序的命令后,就會(huì)產(chǎn)生進(jìn)程,任務(wù)管理器中的每一個(gè)應(yīng)用都是一個(gè)進(jìn)程。谷歌瀏覽器的每個(gè)標(biāo)簽頁和插件都是一個(gè)進(jìn)程。


4.什么是線程

線程是操作系統(tǒng)能夠進(jìn)行資源調(diào)度的最小單位,它被包含在進(jìn)程之中,是進(jìn)程中的實(shí)際運(yùn)作單位,每個(gè)線程執(zhí)行的都是進(jìn)程代碼的某個(gè)片段,特定的線程總是在執(zhí)行特定的任務(wù)。

5.進(jìn)程和線程的關(guān)系

5.1 起源不同

先有進(jìn)程,后有線程。進(jìn)程由于資源利用率、公平性和便利性誕生。處理器的速度往往比外設(shè)的速度快(鍵盤、鼠標(biāo)等),為了提高 CPU 的利用率,誕生了線程,目的就是為了提高程序的執(zhí)行效率。

5.2 概念不同

進(jìn)程是資源分配的最小單位。

線程是程序執(zhí)行的最小單位(線程是操作系統(tǒng)能夠進(jìn)行資源調(diào)度的最小單位,同個(gè)進(jìn)程中的線程也可以被同時(shí)調(diào)度到多個(gè) CPU 上運(yùn)行),線程也被稱為輕量級(jí)進(jìn)程。

5.3 內(nèi)存共享方式不同

默認(rèn)情況下,進(jìn)程的內(nèi)存無法與其他進(jìn)程共享(進(jìn)程間通信通過 IPC 進(jìn)行)。

線程共享由操作系統(tǒng)分配給其父進(jìn)程的內(nèi)存塊。

5.4 擁有資源不同

操作系統(tǒng)為各個(gè)獨(dú)立執(zhí)行的進(jìn)程分配各種資源,包括內(nèi)存,文件句柄以及安全證書等。

線程會(huì)共享進(jìn)程范圍內(nèi)的資源,例如內(nèi)存句柄、文件句柄、進(jìn)程用戶 ID 以及進(jìn)程組 ID 等。每個(gè)線程也有各自獨(dú)立的資源,例如線程 ID、程序計(jì)數(shù)器、棧以及局部變量等。

5.5 數(shù)量不同

一個(gè)程序至少擁有一個(gè)進(jìn)程,一個(gè)進(jìn)程至少擁有一個(gè)線程。

5.6 開銷不同

線程的創(chuàng)建、終止時(shí)間比進(jìn)程短。

同一進(jìn)程內(nèi)的線程切換時(shí)間比進(jìn)程短。

同一進(jìn)程的各個(gè)線程間共享內(nèi)存和文件資源,可以不通過內(nèi)核進(jìn)行通信。

5.7 生命周期類似

進(jìn)程和線程都包含就緒、運(yùn)行、等待狀態(tài)。

6.Java 和多線程的關(guān)系

Java 在設(shè)計(jì)之初就支持了多線程,而且 Java 中的線程會(huì)一對(duì)一映射到操作系統(tǒng)的內(nèi)核線程中(實(shí)際的線程數(shù)量,不是虛擬線程)。除了我們啟動(dòng)的線程,還包括 JVM 自啟動(dòng)線程。


二、多線程

1.什么是多線程

1.1 概念

多線程是指單個(gè)進(jìn)程中運(yùn)行多個(gè)線程,如果一個(gè)程序允許運(yùn)行兩個(gè)或以上的線程,那么它就是多線程程序。

1.2 例子

房間的例子客廳:公共空間廁所:鎖獨(dú)立房間:線程共享空間打掃衛(wèi)生:線程合作

火鍋的例子大火鍋一個(gè)人吃:?jiǎn)芜M(jìn)程單線程大火鍋多個(gè)人吃:?jiǎn)芜M(jìn)程多線程

2. 使用多線程的原因

2.1 發(fā)揮多核處理器的強(qiáng)大能力

充分發(fā)揮多核 CPU 的優(yōu)勢(shì),提高處理器速度。

避免無效等待(進(jìn)行 I/O 操作時(shí)可以處理其他事情)。

提升用戶體驗(yàn)性,避免卡頓,縮短等待時(shí)間并行處理,提高性能,通常用于服務(wù)器(例如 Tomcat),用多個(gè)線程去處理接收的 HTTP 請(qǐng)求。在 Android 開發(fā)中,主線程的任務(wù)之一就是繪制屏幕, 主線程不允許進(jìn)行IO 操作或網(wǎng)絡(luò)請(qǐng)求,目的就是為了避免卡頓,影響用戶的體驗(yàn)。

2.2 便于編程建模

將大的任務(wù)分割為多個(gè)小任務(wù),分別建立程序模型,并通過多線程分別運(yùn)行這幾個(gè)任務(wù)。

2.3 計(jì)算機(jī)的性能定律

摩爾定律失效

摩爾定律——當(dāng)價(jià)格不變時(shí),集成電路上可容納的元器件的數(shù)目每個(gè) 18-24 個(gè)月就會(huì)翻一倍以上,性能也會(huì)提升一倍。

阿姆達(dá)爾定律(Amdahl)登臺(tái)

阿姆達(dá)爾定律:處理器越多,程序執(zhí)行就越快,但有上限,取決于程序中串行部分的比例,并行的比例越高,多處理器的效果越明顯。

最下面藍(lán)色曲線,當(dāng)并行的比例為 50% 時(shí),最快速度可以提升2倍;最上面綠色曲線,當(dāng)并行的比例為 95% 時(shí),最快速度可以提升20倍。


3.多線程使用場(chǎng)景

后臺(tái)線程,如執(zhí)行定時(shí)任務(wù)。

tomcat——每次有一個(gè)新的請(qǐng)求過來的時(shí)候,tomcat 會(huì)把這個(gè)請(qǐng)求交給一個(gè)新的線程去處理。

多線程后臺(tái)并行下載文件。

4.多線程的風(fēng)險(xiǎn)

4.1 安全性問題

當(dāng)多個(gè)線程同時(shí)訪問和修改相同的變量時(shí),將會(huì)在串行編程模型中引入非串行因素,如 i++ 的數(shù)據(jù)錯(cuò)誤。

4.2 活躍性問題

當(dāng)某個(gè)操作無法繼續(xù)執(zhí)行下去的時(shí)候,就會(huì)發(fā)生活躍性問題,如死鎖、饑餓以及活鎖。

4.3 性能問題

在多線程程序中,當(dāng)線程調(diào)度器臨時(shí)掛起活躍線程并轉(zhuǎn)而運(yùn)行另一個(gè)線程時(shí),就會(huì)頻繁得出現(xiàn)上下文切換操作(Context Switch),這種操作會(huì)帶來極大的開銷:保存和恢復(fù)執(zhí)行上下文,丟失局部性,并且 CPU 將更多的時(shí)間花在線程調(diào)度而不是線程運(yùn)行上。當(dāng)線程共享數(shù)據(jù)時(shí),必須使用同步機(jī)制,而這些機(jī)制往往會(huì)抑制某些編譯器優(yōu)化,使內(nèi)存緩沖區(qū)的數(shù)據(jù)無效,以及增加共享內(nèi)存總線的同步流量。這些因素都將帶來額外的性能開銷。

三、串行、并行、并發(fā)


1.串行

串行是將多個(gè)任務(wù)按順序排隊(duì)執(zhí)行,例如:聽完音樂再寫代碼。

2.并行

真正的“同時(shí)”運(yùn)行,在同一時(shí)刻有多個(gè)任務(wù)執(zhí)行,需要多核處理器,因?yàn)閱魏颂幚砥鳠o法在同一時(shí)刻執(zhí)行多個(gè)任務(wù)。例如:邊聽音樂邊寫代碼。

3.并發(fā)

兩個(gè)或多個(gè)任務(wù)可以在重疊時(shí)間段內(nèi)啟動(dòng),運(yùn)行和完成。

并行(兩個(gè)線程同時(shí)執(zhí)行)一定是并發(fā),并發(fā)并不一定是并行。

例如一會(huì)兒聽音樂,一會(huì)兒寫代碼,輪流執(zhí)行。

4.高并發(fā)

4.1 概念

同時(shí)有很多個(gè)請(qǐng)求發(fā)送給服務(wù)器系統(tǒng),服務(wù)器并行處理請(qǐng)求。

4.2 多線程和高并發(fā)

高并發(fā)是一種狀態(tài),多線程是高并發(fā)的一種重要解決方案,高并發(fā)并不意味著多線程。

4.3 高并發(fā)指標(biāo)

QPS(Queries Per Second)

帶寬

PV (Page View)

UV(Unique Visitor)

吞吐率(Requests Per Second)

并發(fā)連接數(shù)(The number of concurrent connections)

服務(wù)器平均請(qǐng)求等待時(shí)間(Time per request: across all concurrent requests)

五、同步、異步、阻塞、非阻塞

1.同步與異步

同步和異步關(guān)注的是消息通信機(jī)制,這里指的是調(diào)用者的行為,表示請(qǐng)求是串行還是并行。

同步(Synchronous):客戶端發(fā)出一個(gè)請(qǐng)求后,一直等到服務(wù)端返回最終的結(jié)果。

異步(Asynchronous):客戶端發(fā)出一個(gè)請(qǐng)求后,還可以發(fā)出另外的請(qǐng)求,不用等待之前請(qǐng)求的結(jié)果返回。

2.阻塞與非阻塞

阻塞非阻塞關(guān)注的程序在等待調(diào)用結(jié)果(消息,返回值)時(shí)的狀態(tài),強(qiáng)調(diào)狀態(tài)。

阻塞:客戶端發(fā)起一個(gè)請(qǐng)求后,當(dāng)前線程會(huì)被掛起,直到服務(wù)端返回結(jié)果。

非阻塞:客戶端發(fā)起一個(gè)請(qǐng)求后,不管服務(wù)器會(huì)不會(huì)立刻返回結(jié)果,當(dāng)前線程都不會(huì)被掛起。

3.例子

水壺?zé)睦?,有兩種水壺,一種普通水壺,只能自己觀察水是否燒開;一種帶提醒的水壺,水燒開會(huì)有聲音提醒。

同步阻塞:用普通水壺?zé)恢钡戎撍畨氐乃疅_。

同步非阻塞:用普通水壺?zé)?,然后去客廳看電視,時(shí)不時(shí)觀察水燒開了沒。

異步阻塞:用帶提醒的水壺?zé)?,一直等著該水壺的水燒開。

異步非阻塞:用帶提醒的水壺?zé)?,然后去玩手機(jī),直到該水壺發(fā)出聲音提醒。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容