<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          并發(fā)編程必看的內(nèi)功心法

          共 4220字,需瀏覽 9分鐘

           ·

          2020-08-13 06:29



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

          可以使用不同的并發(fā)模型來實(shí)現(xiàn)并發(fā)系統(tǒng),并發(fā)模型說的是系統(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)和問題比如進(jìn)程通信、網(wǎng)絡(luò)可能出現(xiàn)異常,或者遠(yuǎn)程機(jī)器掛掉等等。但是一個(gè)并發(fā)模型同樣面臨著比如 CPU 故障、網(wǎng)卡出現(xiàn)問題、硬盤出現(xiàn)問題等。

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

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

          認(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)條件 或者 死鎖 等問題。當(dāng)然,這些問題只是可能會(huì)出現(xiàn),具體實(shí)現(xiàn)方式取決于你是否安全的使用和訪問共享對(duì)象。

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

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

          并發(fā)模型

          并行 Worker

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

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

          并行 Worker 模型是 Java 并發(fā)模型中非常常見的一種模型。許多 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 的過程是異步的。

          并行 Worker 的缺點(diǎn)

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

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

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

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

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

          現(xiàn)代的非阻塞并發(fā)算法可以減少爭用提高性能,但是非阻塞算法比較難以實(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í)行起來卻有一些問題,比如,一個(gè)持久列表會(huì)將新元素添加到列表的開頭,并返回所添加的新元素的引用,但是其他線程仍然只持有列表中先前的第一個(gè)元素的引用,他們看不到新添加的元素。

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

          無狀態(tài)的 worker

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

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

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

          流水線

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

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

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

          使用流水線并發(fā)模型通常被設(shè)計(jì)為非阻塞I/O,也就是說,當(dāng)沒有給 worker 分配任務(wù)時(shí),worker 會(huì)做其他工作。非阻塞I/O 意味著當(dāng) worker 開始 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í)也被稱為 響應(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ù)。

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

          Channels 模型

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

          有的時(shí)候 worker 不需要明確知道接下來的 worker 是誰,他們只需要將作者寫入通道中,監(jiān)聽 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),也就不用無需考慮以為并發(fā)而引起的并發(fā)問題。你甚至可以在實(shí)現(xiàn)上把每個(gè) worker 看成是單線程的一種。

          有狀態(tài) worker

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

          更好的硬件整合

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

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

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

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

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

          函數(shù)性并行

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

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

          JDK 1.7 中的 ForkAndJoinPool 類就實(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ì)帶來額外的開銷。


          ? ? ? ?
          ???
          NIO 知識(shí)點(diǎn)一網(wǎng)打盡
          手把手教你 SQL 優(yōu)化的正確姿勢(shì)!
          讓我們來看看池化技術(shù)有多牛?

          覺得不錯(cuò),點(diǎn)個(gè)在看~

          瀏覽 56
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚欧成人一区视频 | 色五月天综合 | 日韩性爽| 91XXx欧美性缓 | 91av在线播放视频 |