鍍金池/ 教程/ Java/ 并發(fā)編程模型
Slipped Conditions
阻塞隊(duì)列
無阻塞算法
嵌套管程鎖死
Java 并發(fā)性和多線程介紹
死鎖
線程安全及不可變性
并發(fā)編程模型
Java 中的讀/寫鎖
剖析同步器
競態(tài)條件與臨界區(qū)
多線程的優(yōu)點(diǎn)
CAS
線程通信
如何創(chuàng)建并運(yùn)行 java 線程
阿姆達(dá)爾定律
避免死鎖
信號量
多線程的代價(jià)
饑餓和公平
線程池
重入鎖死
Java 中的鎖
Java 內(nèi)存模型
線程安全與共享資源
Java 同步塊

并發(fā)編程模型

并發(fā)系統(tǒng)可以采用多種并發(fā)編程模型來實(shí)現(xiàn)。并發(fā)模型指定了系統(tǒng)中的線程如何通過協(xié)作來完成分配給它們的作業(yè)。不同的并發(fā)模型采用不同的方式拆分作業(yè),同時(shí)線程間的協(xié)作和交互方式也不相同。這篇并發(fā)模型教程將會較深入地介紹目前(2015 年,本文撰寫時(shí)間)比較流行的幾種并發(fā)模型。

并發(fā)模型與分布式系統(tǒng)之間的相似性

本文所描述的并發(fā)模型類似于分布式系統(tǒng)中使用的很多體系結(jié)構(gòu)。在并發(fā)系統(tǒng)中線程之間可以相互通信。在分布式系統(tǒng)中進(jìn)程之間也可以相互通信(進(jìn)程有可能在不同的機(jī)器中)。線程和進(jìn)程之間具有很多相似的特性。這也就是為什么很多并發(fā)模型通常類似于各種分布式系統(tǒng)架構(gòu)。

當(dāng)然,分布式系統(tǒng)在處理網(wǎng)絡(luò)失效、遠(yuǎn)程主機(jī)或進(jìn)程宕掉等方面也面臨著額外的挑戰(zhàn)。但是運(yùn)行在巨型服務(wù)器上的并發(fā)系統(tǒng)也可能遇到類似的問題,比如一塊 CPU 失效、一塊網(wǎng)卡失效或一個(gè)磁盤損壞等情況。雖然出現(xiàn)失效的概率可能很低,但是在理論上仍然有可能發(fā)生。

由于并發(fā)模型類似于分布式系統(tǒng)架構(gòu),因此它們通??梢曰ハ嘟梃b思想。例如,為工作者們(線程)分配作業(yè)的模型一般與分布式系統(tǒng)中的負(fù)載均衡系統(tǒng)比較相似。同樣,它們在日志記錄、失效轉(zhuǎn)移、冪等性等錯誤處理技術(shù)上也具有相似性。

【注:冪等性,一個(gè)冪等操作的特點(diǎn)是其任意多次執(zhí)行所產(chǎn)生的影響均與一次執(zhí)行的影響相同】

并行工作者

第一種并發(fā)模型就是我所說的并行工作者模型。傳入的作業(yè)會被分配到不同的工作者上。下圖展示了并行工作者模型:

http://wiki.jikexueyuan.com/project/java-concurrent/images/1.png" alt="" />

在并行工作者模型中,委派者(Delegator)將傳入的作業(yè)分配給不同的工作者。每個(gè)工作者完成整個(gè)任務(wù)。工作者們并行運(yùn)作在不同的線程上,甚至可能在不同的 CPU 上。

如果在某個(gè)汽車廠里實(shí)現(xiàn)了并行工作者模型,每臺車都會由一個(gè)工人來生產(chǎn)。工人們將拿到汽車的生產(chǎn)規(guī)格,并且從頭到尾負(fù)責(zé)所有工作。

在 Java 應(yīng)用系統(tǒng)中,并行工作者模型是最常見的并發(fā)模型(即使正在轉(zhuǎn)變)。java.util.concurrent包中的許多并發(fā)實(shí)用工具都是設(shè)計(jì)用于這個(gè)模型的。你也可以在 Java 企業(yè)級(J2EE)應(yīng)用服務(wù)器的設(shè)計(jì)中看到這個(gè)模型的蹤跡。

并行工作者模型的優(yōu)點(diǎn)

并行工作者模式的優(yōu)點(diǎn)是,它很容易理解。你只需添加更多的工作者來提高系統(tǒng)的并行度。

例如,如果你正在做一個(gè)網(wǎng)絡(luò)爬蟲,可以試試使用不同數(shù)量的工作者抓取到一定數(shù)量的頁面,然后看看多少數(shù)量的工作者消耗的時(shí)間最短(意味著性能最高)。由于網(wǎng)絡(luò)爬蟲是一個(gè) IO 密集型工作,最終結(jié)果很有可能是你電腦中的每個(gè) CPU 或核心分配了幾個(gè)線程。每個(gè) CPU 若只分配一個(gè)線程可能有點(diǎn)少,因?yàn)樵诘却龜?shù)據(jù)下載的過程中 CPU 將會空閑大量時(shí)間。

并行工作者模型的缺點(diǎn)

并行工作者模型雖然看起來簡單,卻隱藏著一些缺點(diǎn)。接下來的章節(jié)中我會分析一些最明顯的弱點(diǎn)。

共享狀態(tài)可能會很復(fù)雜

在實(shí)際應(yīng)用中,并行工作者模型可能比前面所描述的情況要復(fù)雜得多。共享的工作者經(jīng)常需要訪問一些共享數(shù)據(jù),無論是內(nèi)存中的或者共享的數(shù)據(jù)庫中的。下圖展示了并行工作者模型是如何變得復(fù)雜的:

http://wiki.jikexueyuan.com/project/java-concurrent/images/2.png" alt="" />

有些共享狀態(tài)是在像作業(yè)隊(duì)列這樣的通信機(jī)制下。但也有一些共享狀態(tài)是業(yè)務(wù)數(shù)據(jù),數(shù)據(jù)緩存,數(shù)據(jù)庫連接池等。

一旦共享狀態(tài)潛入到并行工作者模型中,將會使情況變得復(fù)雜起來。線程需要以某種方式存取共享數(shù)據(jù),以確保某個(gè)線程的修改能夠?qū)ζ渌€程可見(數(shù)據(jù)修改需要同步到主存中,不僅僅將數(shù)據(jù)保存在執(zhí)行這個(gè)線程的CPU的緩存中)。線程需要避免竟態(tài),死鎖以及很多其他共享狀態(tài)的并發(fā)性問題。

此外,在等待訪問共享數(shù)據(jù)結(jié)構(gòu)時(shí),線程之間的互相等待將會丟失部分并行性。許多并發(fā)數(shù)據(jù)結(jié)構(gòu)是阻塞的,意味著在任何一個(gè)時(shí)間只有一個(gè)或者很少的線程能夠訪問。這樣會導(dǎo)致在這些共享數(shù)據(jù)結(jié)構(gòu)上出現(xiàn)競爭狀態(tài)。在執(zhí)行需要訪問共享數(shù)據(jù)結(jié)構(gòu)部分的代碼時(shí),高競爭基本上會導(dǎo)致執(zhí)行時(shí)出現(xiàn)一定程度的串行化。

現(xiàn)在的非阻塞并發(fā)算法也許可以降低競爭并提升性能,但是非阻塞算法的實(shí)現(xiàn)比較困難。

可持久化的數(shù)據(jù)結(jié)構(gòu)是另一種選擇。在修改的時(shí)候,可持久化的數(shù)據(jù)結(jié)構(gòu)總是保護(hù)它的前一個(gè)版本不受影響。因此,如果多個(gè)線程指向同一個(gè)可持久化的數(shù)據(jù)結(jié)構(gòu),并且其中一個(gè)線程進(jìn)行了修改,進(jìn)行修改的線程會獲得一個(gè)指向新結(jié)構(gòu)的引用。所有其他線程保持對舊結(jié)構(gòu)的引用,舊結(jié)構(gòu)沒有被修改并且因此保證一致性。Scala 編程包含幾個(gè)持久化數(shù)據(jù)結(jié)構(gòu)。

【注:這里的可持久化數(shù)據(jù)結(jié)構(gòu)不是指持久化存儲,而是一種數(shù)據(jù)結(jié)構(gòu),比如 Java 中的 String 類,以及 CopyOnWriteArrayList 類,具體可參考

雖然可持久化的數(shù)據(jù)結(jié)構(gòu)在解決共享數(shù)據(jù)結(jié)構(gòu)的并發(fā)修改時(shí)顯得很優(yōu)雅,但是可持久化的數(shù)據(jù)結(jié)構(gòu)的表現(xiàn)往往不盡人意。

比如說,一個(gè)可持久化的鏈表需要在頭部插入一個(gè)新的節(jié)點(diǎn),并且返回指向這個(gè)新加入的節(jié)點(diǎn)的一個(gè)引用(這個(gè)節(jié)點(diǎn)指向了鏈表的剩余部分)。所有其他現(xiàn)場仍然保留了這個(gè)鏈表之前的第一個(gè)節(jié)點(diǎn),對于這些線程來說鏈表仍然是為改變的。它們無法看到新加入的元素。

這種可持久化的列表采用鏈表來實(shí)現(xiàn)。不幸的是鏈表在現(xiàn)代硬件上表現(xiàn)的不太好。鏈表中得每個(gè)元素都是一個(gè)獨(dú)立的對象,這些對象可以遍布在整個(gè)計(jì)算機(jī)內(nèi)存中。現(xiàn)代 CPU 能夠更快的進(jìn)行順序訪問,所以你可以在現(xiàn)代的硬件上用數(shù)組實(shí)現(xiàn)的列表,以獲得更高的性能。數(shù)組可以順序的保存數(shù)據(jù)。CPU 緩存能夠一次加載數(shù)組的一大塊進(jìn)行緩存,一旦加載完成 CPU 就可以直接訪問緩存中的數(shù)據(jù)。這對于元素散落在 RAM 中的鏈表來說,不太可能做得到。

無狀態(tài)的工作者

共享狀態(tài)能夠被系統(tǒng)中得其他線程修改。所以工作者在每次需要的時(shí)候必須重讀狀態(tài),以確保每次都能訪問到最新的副本,不管共享狀態(tài)是保存在內(nèi)存中的還是在外部數(shù)據(jù)庫中。工作者無法在內(nèi)部保存這個(gè)狀態(tài)(但是每次需要的時(shí)候可以重讀)稱為無狀態(tài)的。

每次都重讀需要的數(shù)據(jù),將會導(dǎo)致速度變慢,特別是狀態(tài)保存在外部數(shù)據(jù)庫中的時(shí)候。

任務(wù)順序是不確定的

并行工作者模式的另一個(gè)缺點(diǎn)是,作業(yè)執(zhí)行順序是不確定的。無法保證哪個(gè)作業(yè)最先或者最后被執(zhí)行。作業(yè) A 可能在作業(yè) B 之前就被分配工作者了,但是作業(yè) B 反而有可能在作業(yè)A之前執(zhí)行。

并行工作者模式的這種非確定性的特性,使得很難在任何特定的時(shí)間點(diǎn)推斷系統(tǒng)的狀態(tài)。這也使得它也更難(如果不是不可能的話)保證一個(gè)作業(yè)在其他作業(yè)之前被執(zhí)行。

流水線模式

第二種并發(fā)模型我們稱之為流水線并發(fā)模型。我之所以選用這個(gè)名字,只是為了配合“并行工作者”的隱喻。其他開發(fā)者可能會根據(jù)平臺或社區(qū)選擇其他稱呼(比如說反應(yīng)器系統(tǒng),或事件驅(qū)動系統(tǒng))。下圖表示一個(gè)流水線并發(fā)模型:

http://wiki.jikexueyuan.com/project/java-concurrent/images/3.png" alt="" />

類似于工廠中生產(chǎn)線上的工人們那樣組織工作者。每個(gè)工作者只負(fù)責(zé)作業(yè)中的部分工作。當(dāng)完成了自己的這部分工作時(shí)工作者會將作業(yè)轉(zhuǎn)發(fā)給下一個(gè)工作者。每個(gè)工作者在自己的線程中運(yùn)行,并且不會和其他工作者共享狀態(tài)。有時(shí)也被成為無共享并行模型。

通常使用非阻塞的 IO 來設(shè)計(jì)使用流水線并發(fā)模型的系統(tǒng)。非阻塞 IO 意味著,一旦某個(gè)工作者開始一個(gè) IO 操作的時(shí)候(比如讀取文件或從網(wǎng)絡(luò)連接中讀取數(shù)據(jù)),這個(gè)工作者不會一直等待 IO 操作的結(jié)束。IO 操作速度很慢,所以等待 IO 操作結(jié)束很浪費(fèi) CPU 時(shí)間。此時(shí) CPU 可以做一些其他事情。當(dāng) IO 操作完成的時(shí)候,IO 操作的結(jié)果(比如讀出的數(shù)據(jù)或者數(shù)據(jù)寫完的狀態(tài))被傳遞給下一個(gè)工作者。

有了非阻塞 IO,就可以使用 IO 操作確定工作者之間的邊界。工作者會盡可能多運(yùn)行直到遇到并啟動一個(gè) IO 操作。然后交出作業(yè)的控制權(quán)。當(dāng) IO 操作完成的時(shí)候,在流水線上的下一個(gè)工作者繼續(xù)進(jìn)行操作,直到它也遇到并啟動一個(gè) IO 操作。

http://wiki.jikexueyuan.com/project/java-concurrent/images/4.png" alt="" />

在實(shí)際應(yīng)用中,作業(yè)有可能不會沿著單一流水線進(jìn)行。由于大多數(shù)系統(tǒng)可以執(zhí)行多個(gè)作業(yè),作業(yè)從一個(gè)工作者流向另一個(gè)工作者取決于作業(yè)需要做的工作。在實(shí)際中可能會有多個(gè)不同的虛擬流水線同時(shí)運(yùn)行。這是現(xiàn)實(shí)當(dāng)中作業(yè)在流水線系統(tǒng)中可能的移動情況:

http://wiki.jikexueyuan.com/project/java-concurrent/images/5.png" alt="" />

作業(yè)甚至也有可能被轉(zhuǎn)發(fā)到超過一個(gè)工作者上并發(fā)處理。比如說,作業(yè)有可能被同時(shí)轉(zhuǎn)發(fā)到作業(yè)執(zhí)行器和作業(yè)日志器。下圖說明了三條流水線是如何通過將作業(yè)轉(zhuǎn)發(fā)給同一個(gè)工作者(中間流水線的最后一個(gè)工作者)來完成作業(yè):

http://wiki.jikexueyuan.com/project/java-concurrent/images/6.png" alt="" />

流水線有時(shí)候比這個(gè)情況更加復(fù)雜。

反應(yīng)器,事件驅(qū)動系統(tǒng)

采用流水線并發(fā)模型的系統(tǒng)有時(shí)候也稱為反應(yīng)器系統(tǒng)或事件驅(qū)動系統(tǒng)。系統(tǒng)內(nèi)的工作者對系統(tǒng)內(nèi)出現(xiàn)的事件做出反應(yīng),這些事件也有可能來自于外部世界或者發(fā)自其他工作者。事件可以是傳入的 HTTP 請求,也可以是某個(gè)文件成功加載到內(nèi)存中等。在寫這篇文章的時(shí)候,已經(jīng)有很多有趣的反應(yīng)器/事件驅(qū)動平臺可以使用了,并且不久的將來會有更多。比較流行的似乎是這幾個(gè):

  • Vert.x
  • AKKa
  • Node.JS(JavaScript)

我個(gè)人覺得 Vert.x 是相當(dāng)有趣的(特別是對于我這樣使用 Java/JVM 的人來說)

Actors 和 Channels

Actors 和 channels 是兩種比較類似的流水線(或反應(yīng)器/事件驅(qū)動)模型。

在 Actor 模型中每個(gè)工作者被稱為 actor。Actor 之間可以直接異步地發(fā)送和處理消息。Actor 可以被用來實(shí)現(xiàn)一個(gè)或多個(gè)像前文描述的那樣的作業(yè)處理流水線。下圖給出了 Actor 模型:

http://wiki.jikexueyuan.com/project/java-concurrent/images/7.png" alt="" />

而在 Channel 模型中,工作者之間不直接進(jìn)行通信。相反,它們在不同的通道中發(fā)布自己的消息(事件)。其他工作者們可以在這些通道上監(jiān)聽消息,發(fā)送者無需知道誰在監(jiān)聽。下圖給出了 Channel 模型:

http://wiki.jikexueyuan.com/project/java-concurrent/images/8.png" alt="" />

在寫這篇文章的時(shí)候,channel 模型對于我來說似乎更加靈活。一個(gè)工作者無需知道誰在后面的流水線上處理作業(yè)。只需知道作業(yè)(或消息等)需要轉(zhuǎn)發(fā)給哪個(gè)通道。通道上的監(jiān)聽者可以隨意訂閱或者取消訂閱,并不會影響向這個(gè)通道發(fā)送消息的工作者。這使得工作者之間具有松散的耦合。

流水線模型的優(yōu)點(diǎn)

相比并行工作者模型,流水線并發(fā)模型具有幾個(gè)優(yōu)點(diǎn),在接下來的章節(jié)中我會介紹幾個(gè)最大的優(yōu)點(diǎn)。

無需共享的狀態(tài)

工作者之間無需共享狀態(tài),意味著實(shí)現(xiàn)的時(shí)候無需考慮所有因并發(fā)訪問共享對象而產(chǎn)生的并發(fā)性問題。這使得在實(shí)現(xiàn)工作者的時(shí)候變得非常容易。在實(shí)現(xiàn)工作者的時(shí)候就好像是單個(gè)線程在處理工作-基本上是一個(gè)單線程的實(shí)現(xiàn)。

有狀態(tài)的工作者

當(dāng)工作者知道了沒有其他線程可以修改它們的數(shù)據(jù),工作者可以變成有狀態(tài)的。對于有狀態(tài),我是指,它們可以在內(nèi)存中保存它們需要操作的數(shù)據(jù),只需在最后將更改寫回到外部存儲系統(tǒng)。因此,有狀態(tài)的工作者通常比無狀態(tài)的工作者具有更高的性能。

較好的硬件整合(Hardware Conformity)

單線程代碼在整合底層硬件的時(shí)候往往具有更好的優(yōu)勢。首先,當(dāng)能確定代碼只在單線程模式下執(zhí)行的時(shí)候,通常能夠創(chuàng)建更優(yōu)化的數(shù)據(jù)結(jié)構(gòu)和算法。

其次,像前文描述的那樣,單線程有狀態(tài)的工作者能夠在內(nèi)存中緩存數(shù)據(jù)。在內(nèi)存中緩存數(shù)據(jù)的同時(shí),也意味著數(shù)據(jù)很有可能也緩存在執(zhí)行這個(gè)線程的 CPU 的緩存中。這使得訪問緩存的數(shù)據(jù)變得更快。

我說的硬件整合是指,以某種方式編寫的代碼,使得能夠自然地受益于底層硬件的工作原理。有些開發(fā)者稱之為 mechanical sympathy。我更傾向于硬件整合這個(gè)術(shù)語,因?yàn)橛?jì)算機(jī)只有很少的機(jī)械部件,并且能夠隱喻“更好的匹配(match better)”,相比“同情(sympathy)”這個(gè)詞在上下文中的意思,我覺得“conform”這個(gè)詞表達(dá)的非常好。當(dāng)然了,這里有點(diǎn)吹毛求疵了,用自己喜歡的術(shù)語就行。

合理的作業(yè)順序

基于流水線并發(fā)模型實(shí)現(xiàn)的并發(fā)系統(tǒng),在某種程度上是有可能保證作業(yè)的順序的。作業(yè)的有序性使得它更容易地推出系統(tǒng)在某個(gè)特定時(shí)間點(diǎn)的狀態(tài)。更進(jìn)一步,你可以將所有到達(dá)的作業(yè)寫入到日志中去。一旦這個(gè)系統(tǒng)的某一部分掛掉了,該日志就可以用來重頭開始重建系統(tǒng)當(dāng)時(shí)的狀態(tài)。按照特定的順序?qū)⒆鳂I(yè)寫入日志,并按這個(gè)順序作為有保障的作業(yè)順序。下圖展示了一種可能的設(shè)計(jì):

http://wiki.jikexueyuan.com/project/java-concurrent/images/8.png" alt="" />

實(shí)現(xiàn)一個(gè)有保障的作業(yè)順序是不容易的,但往往是可行的。如果可以,它將大大簡化一些任務(wù),例如備份、數(shù)據(jù)恢復(fù)、數(shù)據(jù)復(fù)制等,這些都可以通過日志文件來完成。

流水線模型的缺點(diǎn)

流水線并發(fā)模型最大的缺點(diǎn)是作業(yè)的執(zhí)行往往分布到多個(gè)工作者上,并因此分布到項(xiàng)目中的多個(gè)類上。這樣導(dǎo)致在追蹤某個(gè)作業(yè)到底被什么代碼執(zhí)行時(shí)變得困難。

同樣,這也加大了代碼編寫的難度。有時(shí)會將工作者的代碼寫成回調(diào)處理的形式。若在代碼中嵌入過多的回調(diào)處理,往往會出現(xiàn)所謂的回調(diào)地獄(callback hell)現(xiàn)象。所謂回調(diào)地獄,就是意味著在追蹤代碼在回調(diào)過程中到底做了什么,以及確保每個(gè)回調(diào)只訪問它需要的數(shù)據(jù)的時(shí)候,變得非常困難

使用并行工作者模型可以簡化這個(gè)問題。你可以打開工作者的代碼,從頭到尾優(yōu)美的閱讀被執(zhí)行的代碼。當(dāng)然并行工作者模式的代碼也可能同樣分布在不同的類中,但往往也能夠很容易的從代碼中分析執(zhí)行的順序。

函數(shù)式并行(Functional Parallelism)

第三種并發(fā)模型是函數(shù)式并行模型,這是也最近(2015)討論的比較多的一種模型。函數(shù)式并行的基本思想是采用函數(shù)調(diào)用實(shí)現(xiàn)程序。函數(shù)可以看作是”代理人(agents)“或者”actor“,函數(shù)之間可以像流水線模型(AKA 反應(yīng)器或者事件驅(qū)動系統(tǒng))那樣互相發(fā)送消息。某個(gè)函數(shù)調(diào)用另一個(gè)函數(shù),這個(gè)過程類似于消息發(fā)送。

函數(shù)都是通過拷貝來傳遞參數(shù)的,所以除了接收函數(shù)外沒有實(shí)體可以操作數(shù)據(jù)。這對于避免共享數(shù)據(jù)的競態(tài)來說是很有必要的。同樣也使得函數(shù)的執(zhí)行類似于原子操作。每個(gè)函數(shù)調(diào)用的執(zhí)行獨(dú)立于任何其他函數(shù)的調(diào)用。

一旦每個(gè)函數(shù)調(diào)用都可以獨(dú)立的執(zhí)行,它們就可以分散在不同的 CPU 上執(zhí)行了。這也就意味著能夠在多處理器上并行的執(zhí)行使用函數(shù)式實(shí)現(xiàn)的算法。

Java7 中的 java.util.concurrent 包里包含的 ForkAndJoinPool 能夠幫助我們實(shí)現(xiàn)類似于函數(shù)式并行的一些東西。而 Java8 中并行 streams 能夠用來幫助我們并行的迭代大型集合。記住有些開發(fā)者對 ForkAndJoinPool 進(jìn)行了批判(你可以在我的 ForkAndJoinPool 教程里面看到批評的鏈接)。

函數(shù)式并行里面最難的是確定需要并行的那個(gè)函數(shù)調(diào)用???CPU 協(xié)調(diào)函數(shù)調(diào)用需要一定的開銷。某個(gè)函數(shù)完成的工作單元需要達(dá)到某個(gè)大小以彌補(bǔ)這個(gè)開銷。如果函數(shù)調(diào)用作用非常小,將它并行化可能比單線程、單 CPU 執(zhí)行還慢。

我個(gè)人認(rèn)為(可能不太正確),你可以使用反應(yīng)器或者事件驅(qū)動模型實(shí)現(xiàn)一個(gè)算法,像函數(shù)式并行那樣的方法實(shí)現(xiàn)工作的分解。使用事件驅(qū)動模型可以更精確的控制如何實(shí)現(xiàn)并行化(我的觀點(diǎn))。

此外,將任務(wù)拆分給多個(gè) CPU 時(shí)協(xié)調(diào)造成的開銷,僅僅在該任務(wù)是程序當(dāng)前執(zhí)行的唯一任務(wù)時(shí)才有意義。但是,如果當(dāng)前系統(tǒng)正在執(zhí)行多個(gè)其他的任務(wù)時(shí)(比如 web 服務(wù)器,數(shù)據(jù)庫服務(wù)器或者很多其他類似的系統(tǒng)),將單個(gè)任務(wù)進(jìn)行并行化是沒有意義的。不管怎樣計(jì)算機(jī)中的其他 CPU 們都在忙于處理其他任務(wù),沒有理由用一個(gè)慢的、函數(shù)式并行的任務(wù)去擾亂它們。使用流水線(反應(yīng)器)并發(fā)模型可能會更好一點(diǎn),因?yàn)樗_銷更?。ㄔ趩尉€程模式下順序執(zhí)行)同時(shí)能更好的與底層硬件整合。

使用那種并發(fā)模型最好?

所以,用哪種并發(fā)模型更好呢?

通常情況下,這個(gè)答案取決于你的系統(tǒng)打算做什么。如果你的作業(yè)本身就是并行的、獨(dú)立的并且沒有必要共享狀態(tài),你可能會使用并行工作者模型去實(shí)現(xiàn)你的系統(tǒng)。雖然許多作業(yè)都不是自然并行和獨(dú)立的。對于這種類型的系統(tǒng),我相信使用流水線并發(fā)模型能夠更好的發(fā)揮它的優(yōu)勢,而且比并行工作者模型更有優(yōu)勢。

你甚至不用親自編寫所有流水線模型的基礎(chǔ)結(jié)構(gòu)。像 Vert.x 這種現(xiàn)代化的平臺已經(jīng)為你實(shí)現(xiàn)了很多。我也會去為探索如何設(shè)計(jì)我的下一個(gè)項(xiàng)目,使它運(yùn)行在像 Vert.x 這樣的優(yōu)秀平臺上。我感覺 Java EE 已經(jīng)沒有任何優(yōu)勢了。

上一篇:多線程的代價(jià)下一篇:CAS