您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關Fork跟Join原理是什么,文章內容質量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。
只聽到P8大佬不急不慢問道:談談對JDK并發工具的認識?
我開始仔細梳理多年的并發八股文經驗,道:
線程池、Future、CompletableFuture和CompletionService這些并發工具都是幫助SE站在任務角度解決并發問題,而非糾結于線程之間協作的細節,比如線程之間如何實現等待、通知。
簡單并行任務
線程池+Future 組合拳
任務間有聚合關系
AND、OR聚合,CompletableFuture 一招鮮
批量的并行任務
CompletionService 一把梭
并發編程可分為三個層面問題:分工、協作、互斥。
當關注于任務時,你會發現你的視角已躍出并發編程細節,而使用現實世界思維模式,類比現實世界的分工,其實線程池、Future、CompletableFuture和CompletionService都可列為分工問題。
簡單并行任務、聚合任務和批量并行任務的現實的工作流程圖
這三種任務模型,基本覆蓋日常工作中的并發場景,但其實還有一種“分治”任務模型。
分治,分而治之,一種解決復雜問題的思維方法和模式。把一個復雜問題分解成多個相似的子問題,然后再把子問題分解成更小的子問題,直到子問題簡單到可以直接求解。理論上解決每一個問題都對應著一個任務,所以對于問題的分治,實際上就是對于任務的分治。
P8 大佬直接開問,那你說說什么是分治任務模型?
分治任務模型可分為兩個階段:
任務分解
將任務迭代地分解為子任務,直至子任務可計算出結果。將地區具體事務分屬各個地方行政官。
結果合并
逐層合并子任務的執行結果,直至獲得最終結果。各地方行政官最終將治理成果匯報上級。
就像官僚制度一樣:
那你平時開發是如何使用Fork/Join的?
我,我平時還真沒通過啊,就背過。還好這道題,我面試前也準備了…
Fork/Join是一個并行計算框架,以支持分治任務模型
Fork對應分治任務模型里的任務分解
Join對應結果合并
Fork/Join計算框架主要包含兩部分:
分治任務的線程池ForkJoinPool
分治任務ForkJoinTask
這倆的關系類似于 ThreadPoolExecutor 和 Runnable,都是提交任務到線程池,只不過分治任務有自己獨特的任務類型ForkJoinTask。
ForkJoinTask
JDK7 提供,一個抽象類,核心方法如下:
fork()
異步執行一個子任務
join()
阻塞當前線程來等待子任務的執行結果
ForkJoinTask有兩個子類——RecursiveAction和RecursiveTask,顯然都是用遞歸處理分治任務。這兩個子類都定義了抽象方法compute():
RecursiveAction#compute()無返回值
RecursiveTask#compute()有返回值
注意到這倆類都是抽象類,使用要定義子類實現。
只見 P8 開始冷笑,看來要問源碼級別原理了!
那你說下Fork/Join的工作原理
還好我知道阿里面試套路,凡是 java 工具,必問深入的源碼。
因為Fork/Join的核心就是ForkJoinPool,讓我來深入講解ForkJoinPool原理。
ThreadPoolExecutor本質是個生產者-消費者實現,內部有一個任務隊列,作為生產者和消費者的通信媒介。ThreadPoolExecutor可以有多個工作線程,這些工作線程都共享一個任務隊列。
ForkJoinPool本質上也是一個生產者-消費者的實現,但更智能
ForkJoinPool工作原理圖
ThreadPoolExecutor內部只有一個任務隊列,而ForkJoinPool內部有多個任務隊列,當調用ForkJoinPool#invoke()或submit()提交任務時,ForkJoinPool把任務通過路由規則提交到一個任務隊列,如果任務在執行過程中會創建出子任務,那么子任務會提交到工作線程對應的任務隊列。
如果工作線程對應的任務隊列空,是不是就沒活兒干了?
No!ForkJoinPool有個“任務竊取”機制,若工作線程空閑了,它會“竊取”其他工作任務隊列里的任務,例如剛才那個圖中,線程T2對應任務隊列已空
那它會“竊取”線程T1對應的任務隊列的任務。這樣所有工作線程都不會閑著。
ForkJoinPool的任務隊列采用的是雙端隊列,工作線程正常獲取任務和“竊取任務”分別從任務隊列不同的端消費,這也能避免很多不必要的數據競爭。
ForkJoinPool支持任務竊取機制,能夠讓所有線程的工作量基本公平,不會出現線程有的很忙,有的一直在摸魚,所以性能很好,是個很公正的領導。
Java8的Stream API里面并行流也是基于ForkJoinPool。
默認,所有的并行流計算都共享一個ForkJoinPool,這個共享的ForkJoinPool的默認線程數是CPU核數;
若所有并行流計算都是CPU密集型,完全沒有問題,但若存在I/O密集型并行流計算,那很可能因為一個很慢的I/O計算而拖慢整個系統的性能。所以建議用不同ForkJoinPool執行不同類型的計算任務。
關于Fork跟Join原理是什么就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。