一、進(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)簽頁(yè)和插件都是一個(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é)缓笕ネ媸謾C(jī),直到該水壺發(fā)出聲音提醒。
《Java并發(fā)編程實(shí)戰(zhàn)》