自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

一文帶你領(lǐng)略并發(fā)編程的內(nèi)功心法

開(kāi)發(fā) 后端
并發(fā)模型其實(shí)和分布式系統(tǒng)模型非常相似,在并發(fā)模型中是線程彼此進(jìn)行通信,而在分布式系統(tǒng)模型中是 進(jìn)程 彼此進(jìn)行通信。然而本質(zhì)上,進(jìn)程和線程也非常相似。這也就是為什么并發(fā)模型和分布式模型非常相似的原因。

 [[336857]]

本篇文章我們來(lái)探討一下并發(fā)設(shè)計(jì)模型。

可以使用不同的并發(fā)模型來(lái)實(shí)現(xiàn)并發(fā)系統(tǒng),并發(fā)模型說(shuō)的是系統(tǒng)中的線程如何協(xié)作完成并發(fā)任務(wù)。不同的并發(fā)模型以不同的方式拆分任務(wù),線程可以以不同的方式進(jìn)行通信和協(xié)作。

并發(fā)模型和分布式系統(tǒng)很相似

并發(fā)模型其實(shí)和分布式系統(tǒng)模型非常相似,在并發(fā)模型中是線程彼此進(jìn)行通信,而在分布式系統(tǒng)模型中是 進(jìn)程 彼此進(jìn)行通信。然而本質(zhì)上,進(jìn)程和線程也非常相似。這也就是為什么并發(fā)模型和分布式模型非常相似的原因。

分布式系統(tǒng)通常要比并發(fā)系統(tǒng)面臨更多的挑戰(zhàn)和問(wèn)題比如進(jìn)程通信、網(wǎng)絡(luò)可能出現(xiàn)異常,或者遠(yuǎn)程機(jī)器掛掉等等。但是一個(gè)并發(fā)模型同樣面臨著比如 CPU 故障、網(wǎng)卡出現(xiàn)問(wèn)題、硬盤(pán)出現(xiàn)問(wèn)題等。

因?yàn)椴l(fā)模型和分布式模型很相似,因此他們可以相互借鑒,例如用于線程分配的模型就類(lèi)似于分布式系統(tǒng)環(huán)境中的負(fù)載均衡模型。

其實(shí)說(shuō)白了,分布式模型的思想就是借鑒并發(fā)模型的基礎(chǔ)上推演發(fā)展來(lái)的。

認(rèn)識(shí)兩個(gè)狀態(tài)

并發(fā)模型的一個(gè)重要的方面是,線程是否應(yīng)該共享狀態(tài),是具有共享狀態(tài)還是獨(dú)立狀態(tài)。共享狀態(tài)也就意味著在不同線程之間共享某些狀態(tài)

狀態(tài)其實(shí)就是數(shù)據(jù),比如一個(gè)或者多個(gè)對(duì)象。當(dāng)線程要共享數(shù)據(jù)時(shí),就會(huì)造成 競(jìng)態(tài)條件 或者 死鎖 等問(wèn)題。當(dāng)然,這些問(wèn)題只是可能會(huì)出現(xiàn),具體實(shí)現(xiàn)方式取決于你是否安全的使用和訪問(wèn)共享對(duì)象。

 

獨(dú)立的狀態(tài)表明狀態(tài)不會(huì)在多個(gè)線程之間共享,如果線程之間需要通信的話,他們可以訪問(wèn)不可變的對(duì)象來(lái)實(shí)現(xiàn),這是一種最有效的避免并發(fā)問(wèn)題的一種方式,如下圖所示

 

使用獨(dú)立狀態(tài)讓我們的設(shè)計(jì)更加簡(jiǎn)單,因?yàn)橹挥幸粋€(gè)線程能夠訪問(wèn)對(duì)象,即使交換對(duì)象,也是不可變的對(duì)象。

并發(fā)模型

并行 Worker

第一個(gè)并發(fā)模型是并行 worker 模型,客戶(hù)端會(huì)把任務(wù)交給 代理人(Delegator),然后由代理人把工作分配給不同的 工人(worker)。如下圖所示

 

并行 worker 的核心思想是,它主要有兩個(gè)進(jìn)程即代理人和工人,Delegator 負(fù)責(zé)接收來(lái)自客戶(hù)端的任務(wù)并把任務(wù)下發(fā),交給具體的 Worker 進(jìn)行處理,Worker 處理完成后把結(jié)果返回給 Delegator,在 Delegator 接收到 Worker 處理的結(jié)果后對(duì)其進(jìn)行匯總,然后交給客戶(hù)端。

并行 Worker 模型是 Java 并發(fā)模型中非常常見(jiàn)的一種模型。許多 java.util.concurrent 包下的并發(fā)工具都使用了這種模型。

并行 Worker 的優(yōu)點(diǎn)

并行 Worker 模型的一個(gè)非常明顯的特點(diǎn)就是很容易理解,為了提高系統(tǒng)的并行度你可以增加多個(gè) Worker 完成任務(wù)。

并行 Worker 模型的另外一個(gè)好處就是,它會(huì)將一個(gè)任務(wù)拆分成多個(gè)小任務(wù),并發(fā)執(zhí)行,Delegator 在接受到 Worker 的處理結(jié)果后就會(huì)返回給 Client,整個(gè) Worker -> Delegator -> Client 的過(guò)程是異步的。

并行 Worker 的缺點(diǎn)

同樣的,并行 Worker 模式同樣會(huì)有一些隱藏的缺點(diǎn)

共享狀態(tài)會(huì)變得很復(fù)雜

實(shí)際的并行 Worker 要比我們圖中畫(huà)出的更復(fù)雜,主要是并行 Worker 通常會(huì)訪問(wèn)內(nèi)存或共享數(shù)據(jù)庫(kù)中的某些共享數(shù)據(jù)。

 

 


 

 

這些共享狀態(tài)可能會(huì)使用一些工作隊(duì)列來(lái)保存業(yè)務(wù)數(shù)據(jù)、數(shù)據(jù)緩存、數(shù)據(jù)庫(kù)的連接池等。在線程通信中,線程需要確保共享狀態(tài)是否能夠讓其他線程共享,而不是僅僅停留在 CPU 緩存中讓自己可用,當(dāng)然這些都是程序員在設(shè)計(jì)時(shí)就需要考慮的問(wèn)題。線程需要避免 競(jìng)態(tài)條件,死鎖 和許多其他共享狀態(tài)造成的并發(fā)問(wèn)題。

多線程在訪問(wèn)共享數(shù)據(jù)時(shí),會(huì)丟失并發(fā)性,因?yàn)椴僮飨到y(tǒng)要保證只有一個(gè)線程能夠訪問(wèn)數(shù)據(jù),這會(huì)導(dǎo)致共享數(shù)據(jù)的爭(zhēng)用和搶占。未搶占到資源的線程會(huì) 阻塞。

現(xiàn)代的非阻塞并發(fā)算法可以減少爭(zhēng)用提高性能,但是非阻塞算法比較難以實(shí)現(xiàn)。

可持久化的數(shù)據(jù)結(jié)構(gòu)(Persistent data structures) 是另外一個(gè)選擇。可持久化的數(shù)據(jù)結(jié)構(gòu)在修改后始終會(huì)保留先前版本。因此,如果多個(gè)線程同時(shí)修改一個(gè)可持久化的數(shù)據(jù)結(jié)構(gòu),并且一個(gè)線程對(duì)其進(jìn)行了修改,則修改的線程會(huì)獲得對(duì)新數(shù)據(jù)結(jié)構(gòu)的引用。

雖然可持久化的數(shù)據(jù)結(jié)構(gòu)是一個(gè)新的解決方法,但是這種方法實(shí)行起來(lái)卻有一些問(wèn)題,比如,一個(gè)持久列表會(huì)將新元素添加到列表的開(kāi)頭,并返回所添加的新元素的引用,但是其他線程仍然只持有列表中先前的第一個(gè)元素的引用,他們看不到新添加的元素。

持久化的數(shù)據(jù)結(jié)構(gòu)比如 鏈表(LinkedList) 在硬件性能上表現(xiàn)不佳。列表中的每個(gè)元素都是一個(gè)對(duì)象,這些對(duì)象散布在計(jì)算機(jī)內(nèi)存中?,F(xiàn)代 CPU 的順序訪問(wèn)往往要快的多,因此使用數(shù)組等順序訪問(wèn)的數(shù)據(jù)結(jié)構(gòu)則能夠獲得更高的性能。CPU 高速緩存可以將一個(gè)大的矩陣塊加載到高速緩存中,并讓 CPU 在加載后直接訪問(wèn) CPU 高速緩存中的數(shù)據(jù)。對(duì)于鏈表,將元素分散在整個(gè) RAM 上,這實(shí)際上是不可能的。

無(wú)狀態(tài)的 worker

共享狀態(tài)可以由其他線程所修改,因此,worker 必須在每次操作共享狀態(tài)時(shí)重新讀取,以確保在副本上能夠正確工作。不在線程內(nèi)部保持狀態(tài)的 worker 成為無(wú)狀態(tài)的 worker。

作業(yè)順序是不確定的

并行工作模型的另一個(gè)缺點(diǎn)是作業(yè)的順序不確定,無(wú)法保證首先執(zhí)行或最后執(zhí)行哪些作業(yè)。任務(wù) A 在任務(wù) B 之前分配給 worker,但是任務(wù) B 可能在任務(wù) A 之前執(zhí)行。

流水線

第二種并發(fā)模型就是我們經(jīng)常在生產(chǎn)車(chē)間遇到的 流水線并發(fā)模型,下面是流水線設(shè)計(jì)模型的流程圖

 

這種組織架構(gòu)就像是工廠中裝配線中的 worker,每個(gè) worker 只完成全部工作的一部分,完成一部分后,worker 會(huì)將工作轉(zhuǎn)發(fā)給下一個(gè) worker。

每道程序都在自己的線程中運(yùn)行,彼此之間不會(huì)共享狀態(tài),這種模型也被稱(chēng)為無(wú)共享并發(fā)模型。

使用流水線并發(fā)模型通常被設(shè)計(jì)為非阻塞I/O,也就是說(shuō),當(dāng)沒(méi)有給 worker 分配任務(wù)時(shí),worker 會(huì)做其他工作。非阻塞I/O 意味著當(dāng) worker 開(kāi)始 I/O 操作,例如從網(wǎng)絡(luò)中讀取文件,worker 不會(huì)等待 I/O 調(diào)用完成。因?yàn)?I/O 操作很慢,所以等待 I/O 非常耗費(fèi)時(shí)間。在等待 I/O 的同時(shí),CPU 可以做其他事情,I/O 操作完成后的結(jié)果將傳遞給下一個(gè) worker。下面是非阻塞 I/O 的流程圖

 

在實(shí)際情況中,任務(wù)通常不會(huì)按著一條裝配線流動(dòng),由于大多數(shù)程序需要做很多事情,因此需要根據(jù)完成的不同工作在不同的 worker 之間流動(dòng),如下圖所示

 

任務(wù)還可能需要多個(gè) worker 共同參與完成

 

響應(yīng)式 - 事件驅(qū)動(dòng)系統(tǒng)

使用流水線模型的系統(tǒng)有時(shí)也被稱(chēng)為 響應(yīng)式 或者 事件驅(qū)動(dòng)系統(tǒng),這種模型會(huì)根據(jù)外部的事件作出響應(yīng),事件可能是某個(gè) HTTP 請(qǐng)求或者某個(gè)文件完成加載到內(nèi)存中。

Actor 模型

在 Actor 模型中,每一個(gè) Actor 其實(shí)就是一個(gè) Worker, 每一個(gè) Actor 都能夠處理任務(wù)。

簡(jiǎn)單來(lái)說(shuō),Actor 模型是一個(gè)并發(fā)模型,它定義了一系列系統(tǒng)組件應(yīng)該如何動(dòng)作和交互的通用規(guī)則,最著名的使用這套規(guī)則的編程語(yǔ)言是 Erlang。一個(gè)參與者Actor對(duì)接收到的消息做出響應(yīng),然后可以創(chuàng)建出更多的 Actor 或發(fā)送更多的消息,同時(shí)準(zhǔn)備接收下一條消息。

 

Channels 模型

在 Channel 模型中,worker 通常不會(huì)直接通信,與此相對(duì)的,他們通常將事件發(fā)送到不同的 通道(Channel)上,然后其他 worker 可以在這些通道上獲取消息,下面是 Channel 的模型圖

 

有的時(shí)候 worker 不需要明確知道接下來(lái)的 worker 是誰(shuí),他們只需要將作者寫(xiě)入通道中,監(jiān)聽(tīng) Channel 的 worker 可以訂閱或者取消訂閱,這種方式降低了 worker 和 worker 之間的耦合性。

流水線設(shè)計(jì)的優(yōu)點(diǎn)

與并行設(shè)計(jì)模型相比,流水線模型具有一些優(yōu)勢(shì),具體優(yōu)勢(shì)如下

不會(huì)存在共享狀態(tài)

因?yàn)榱魉€設(shè)計(jì)能夠保證 worker 在處理完成后再傳遞給下一個(gè) worker,所以 worker 與 worker 之間不需要共享任何狀態(tài),也就不用無(wú)需考慮以為并發(fā)而引起的并發(fā)問(wèn)題。你甚至可以在實(shí)現(xiàn)上把每個(gè) worker 看成是單線程的一種。

有狀態(tài) worker

因?yàn)?worker 知道沒(méi)有其他線程修改自身的數(shù)據(jù),所以流水線設(shè)計(jì)中的 worker 是有狀態(tài)的,有狀態(tài)的意思是他們可以將需要操作的數(shù)據(jù)保留在內(nèi)存中,有狀態(tài)通常比無(wú)狀態(tài)更快。

更好的硬件整合

因?yàn)槟憧梢园蚜魉€看成是單線程的,而單線程的工作優(yōu)勢(shì)在于它能夠和硬件的工作方式相同。因?yàn)橛袪顟B(tài)的 worker 通常在 CPU 中緩存數(shù)據(jù),這樣可以更快地訪問(wèn)緩存的數(shù)據(jù)。

使任務(wù)更加有效的進(jìn)行

可以對(duì)流水線并發(fā)模型中的任務(wù)進(jìn)行排序,一般用來(lái)日志的寫(xiě)入和恢復(fù)。

流水線設(shè)計(jì)的缺點(diǎn)

流水線并發(fā)模型的缺點(diǎn)是任務(wù)會(huì)涉及多個(gè) worker,因此可能會(huì)分散在項(xiàng)目代碼的多個(gè)類(lèi)中。因此很難確定每個(gè) worker 都在執(zhí)行哪個(gè)任務(wù)。流水線的代碼編寫(xiě)也比較困難,設(shè)計(jì)許多嵌套回調(diào)處理程序的代碼通常被稱(chēng)為 回調(diào)地獄。回調(diào)地獄很難追蹤 debug。

函數(shù)性并行

函數(shù)性并行模型是最近才提出的一種并發(fā)模型,它的基本思路是使用函數(shù)調(diào)用來(lái)實(shí)現(xiàn)。消息的傳遞就相當(dāng)于是函數(shù)的調(diào)用。傳遞給函數(shù)的參數(shù)都會(huì)被拷貝,因此在函數(shù)之外的任何實(shí)體都無(wú)法操縱函數(shù)內(nèi)的數(shù)據(jù)。這使得函數(shù)執(zhí)行類(lèi)似于原子操作。每個(gè)函數(shù)調(diào)用都可以獨(dú)立于任何其他函數(shù)調(diào)用執(zhí)行。

當(dāng)每個(gè)函數(shù)調(diào)用獨(dú)立執(zhí)行時(shí),每個(gè)函數(shù)都可以在單獨(dú)的 CPU 上執(zhí)行。這也就是說(shuō),函數(shù)式并行并行相當(dāng)于是各個(gè) CPU 單獨(dú)執(zhí)行各自的任務(wù)。

JDK 1.7 中的 ForkAndJoinPool 類(lèi)就實(shí)現(xiàn)了函數(shù)性并行的功能。Java 8 提出了 stream 的概念,使用并行流也能夠?qū)崿F(xiàn)大量集合的迭代。

函數(shù)性并行的難點(diǎn)是要知道函數(shù)的調(diào)用流程以及哪些 CPU 執(zhí)行了哪些函數(shù),跨 CPU 函數(shù)調(diào)用會(huì)帶來(lái)額外的開(kāi)銷(xiāo)。

本文轉(zhuǎn)載自微信公眾號(hào)「Java建設(shè)者」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系Java建設(shè)者公眾號(hào)。

 

責(zé)任編輯:武曉燕 來(lái)源: Java建設(shè)者
相關(guān)推薦

2020-11-10 09:20:40

開(kāi)發(fā)模式代碼

2019-08-06 09:00:00

JavaScript函數(shù)式編程前端

2022-12-20 07:39:46

2023-11-20 08:18:49

Netty服務(wù)器

2023-12-21 17:11:21

Containerd管理工具命令行

2023-11-06 08:16:19

APM系統(tǒng)運(yùn)維

2021-05-29 10:11:00

Kafa數(shù)據(jù)業(yè)務(wù)

2023-07-31 08:18:50

Docker參數(shù)容器

2022-11-11 19:09:13

架構(gòu)

2025-04-28 02:22:00

2022-02-24 07:34:10

SSL協(xié)議加密

2023-11-08 08:15:48

服務(wù)監(jiān)控Zipkin

2023-10-27 08:15:45

2024-05-22 09:45:49

2021-09-13 22:34:56

區(qū)塊鏈新基建數(shù)字化轉(zhuǎn)型

2023-03-06 21:29:41

mmap技術(shù)操作系統(tǒng)

2022-05-16 10:49:28

網(wǎng)絡(luò)協(xié)議數(shù)據(jù)

2019-06-13 21:31:19

AI

2022-04-08 09:01:14

CSS自定義屬性前端

2020-11-27 09:40:53

Rollup前端代碼
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)