執行緒 十三 ForkJoin

2021-10-23 18:19:45 字數 2510 閱讀 8010

到這裡,關於 completablefuture 的基本使用你已經了解的差不多了,不知道你是否注意,我們前面說的帶有 sync 的方法是單獨起乙個執行緒來執行,但是我們並沒有建立執行緒,這是怎麼實現的呢?細心的朋友如果仔細看每個變種函式的第三個方法也許會發現裡面都有乙個 executor 型別的引數,用於指定執行緒池,因為實際業務中我們是嚴禁手動建立執行緒的,如果沒有指定執行緒池,那自然就會有乙個預設的執行緒池,也就是 forkjoinpool。

forkjoinpool 的執行緒數預設是 cpu 的核心數。但是,最好不要所有業務共用乙個執行緒池,因為,一旦有任務執行一些很慢的 i/o 操作,就會導致執行緒池中所有執行緒都阻塞在 i/o 操作上,從而造成執行緒飢餓,進而影響整個系統的效能。

什麼是fork/join呢,它的原理是啥,在這整理記錄一把。

使用fork/join介紹

簡單的說forkjoin就是在必要的情況下,將乙個大任務,進行拆分成若干個小任務(拆到不可再拆時),再將乙個個小的任務運算的結果進行jion彙總。通過網上的一張圖來理解fork/join,

使用fork/join框架

1.繼承recursivetask或者recursiveaction,重寫compute()方法。recursivetask有返回值,recursiveaction沒有返回值

2.要執行乙個forkjoin的任務,首先建乙個執行緒池forkjoinpool,使用執行緒池執行任務。

3.使用fork方法拆分任務。

4.使用forkjoinpool呼叫invoke方法來執行乙個任務。

5.join合併計算結果。

為了對比下使用fork/join和普通迴圈在效率方面的區別,再新增乙個迴圈計算的方法,**示例如下:

public static void main(string args) 

static class forkjoinclass extends recursivetask

@override

protected long compute()

return sum;

}else }}

static void userforloop()

system.out.println(sum);

long endtime = system.currenttimemillis();

system.out.println("使用普通迴圈耗時:" + (endtime - starttime));

}

執行結果如下:

從執行結果上看,普通迴圈比使用fork/join還要快,forkjoin拆分合併任務也是需要時間的,對於計算量比較小的任務,拆分合併所花費的時間可能會大於計算時間,這時候用forkjoin拆分任務就會有點得不償失了,如果將需要計算的結果增大到10000000000l就可以看出使用fork/join耗時明顯小於普通迴圈。

注意:使用forkjoin時,任務的量一定要大,否則太小,forkjoin拆分合併任務也是需要時間的,對於計算量比較小的任務,拆分合併所花費的時間可能會大於計算時間,效率不一定會高。

fork/join框架與傳統執行緒池的區別

forkjoin框架採用的是「工作竊取模式」,傳統執行緒在處理任務時,假設有乙個大任務被分解成了20個小任務,並由四個執行緒a,b,c,d處理,理論上來講乙個執行緒處理5個任務,每個執行緒的任務都放在乙個佇列中,當b,c,d的任務都處理完了,而a因為某些原因阻塞在了第二個小任務上,那麼b,c,d都需要等待a處理完成,此時a處理完第二個任務後還有三個任務需要處理,可想而知,這樣cpu的利用率很低。而forkjoin採取的模式是,當b,c,d都處理完了,而a還阻塞在第二個任務時,b會從a的任務佇列的末尾偷取乙個任務過來自己處理,c和d也會從a的任務佇列的末尾偷乙個任務,這樣就相當於b,c,d額外幫a分擔了一些任務,提高了cpu的利用率。

fork/join工作竊取模式

先通過乙個圖理解工作竊取模式,如下:

如上圖,工作執行緒2,3,4已經執行完任務處於空閒狀態,為了方便理解,任務以數字表示工作任務量,工作執行緒1還有1個任務量為4的任務要執行,fork為兩個任務量為2的任務,這時候工作執行緒2竊取乙個任務,這時執行緒1,2分別有乙個工作量為2的任務,繼續拆分後,分別拆分為兩個任務量為1的任務,分別被工作執行緒3和4竊取乙個任務,其實最後執行緒1,2,3,4分別執行了乙個任務,理論上除去fork,join操作的時間,效率會提高4倍。

執行緒的併發工具類Fork Join

快速排序,歸併排序,二分查詢屬於分而治之。分治法的設計思想是 將乙個難以直接解決的大問題,分豁成一些規模較小的相同問題,以便 各個擊破,分而治之。通常我們不直接繼承forkjointask類,只需要直接繼承其子類。1.recursiveaction 用於沒有返回結果的任務,2.recursiveta...

fork join例子說明

public static class selectmaxproblem public int solvesequentially return max public selectmaxproblem subproblem int substart,int subend override publi...

(十三)QT的執行緒

qt實際是有兩種執行緒寫法的 第一種就是常見的寫法 繼承qthread類,重寫run函式,把需要子執行緒執行的任務放在run函式中,呼叫start,執行緒開始執行。這種寫法有個缺點就是 需要執行緒執行的任務都要放在run函式中,如果你在run中放乙個signal,通知run函式外的槽函式執行某個操作...