我的Java開發學習之旅------>Java使用Fork/Join架構來並存執行任務

來源:互聯網
上載者:User

標籤:forkjoin   forkjoinpool   recursivetask   recursiveaction   

現代的電腦已經向多CPU方向發展,即使是普通的PC,甚至現在的智能手機、多核處理器已被廣泛應用。在未來,處理器的核心數將會發展的越來越多。

雖然硬體上的多核CPU已經十分成熟,但是很多應用程式並未這種多核CPU做好準備,因此並不能很好地利用多核CPU的效能優勢。

為了充分利用多CPU、多核CPU的效能優勢,級軟基軟體系統應該可以充分“挖掘”每個CPU的計算能力,決不能讓某個CPU處於“空閑”狀態。為此,可以考慮把一個任務拆分成多個“小任務”,把多個"小任務"放到多個處理器核心上並存執行。當多個“小任務”執行完成之後,再將這些執行結果合并起來即可。

如下面的所示:


第一步分割任務。首先我們需要有一個fork類來把大任務分割成子任務,有可能子任務還是很大,所以還需要不停的分割,直到分割出的子任務足夠小。

第二步執行任務併合並結果。分割的子任務分別放在雙端隊列裡,然後幾個啟動線程分別從雙端隊列裡擷取任務執行。子任務執行完的結果都統一放在一個隊列裡,啟動一個線程從隊列裡拿資料,然後合并這些資料。


Java提供了ForkJoinPool來支援將一個任務拆分成多個“小任務”並行計算,再把多個“小任務”的結果合成總的計算結果。

ForkJoinPool是ExecutorService的實作類別,因此是一種特殊的線程池。ForkJoinPool提供了如下兩個常用的構造器。

  •  public ForkJoinPool(int parallelism):建立一個包含parallelism個並行線程的ForkJoinPool
  •  public ForkJoinPool() :以Runtime.getRuntime().availableProcessors()的返回值作為parallelism來建立ForkJoinPool

建立ForkJoinPool執行個體後,可以釣魚ForkJoinPool的submit(ForkJoinTask<T> task)或者invoke(ForkJoinTask<T> task)來執行指定任務。其中ForkJoinTask代表一個可以並行、合并的任務。ForkJoinTask是一個抽象類別,它有兩個抽象子類:RecursiveAction和RecursiveTask。

  • RecursiveTask代表有返回值的任務
  • RecursiveAction代表沒有返回值的任務。


一、RecursiveAction

下面以一個沒有返回值的大任務為例,介紹一下RecursiveAction的用法。

大任務是:列印0-200的數值。

小任務是:每次只能列印50個數值。

import java.util.concurrent.ForkJoinPool;import java.util.concurrent.RecursiveAction;import java.util.concurrent.TimeUnit;//RecursiveAction為ForkJoinTask的抽象子類,沒有傳回值的任務class PrintTask extends RecursiveAction {// 每個"小任務"最多隻列印50個數private static final int MAX = 50;private int start;private int end;PrintTask(int start, int end) {this.start = start;this.end = end;}@Overrideprotected void compute() {// 當end-start的值小於MAX時候,開始列印if ((end - start) < MAX) {for (int i = start; i < end; i++) {System.out.println(Thread.currentThread().getName() + "的i值:"+ i);}} else {// 將大任務分解成兩個小任務int middle = (start + end) / 2;PrintTask left = new PrintTask(start, middle);PrintTask right = new PrintTask(middle, end);// 並存執行兩個小任務left.fork();right.fork();}}}public class ForkJoinPoolTest {/** * @param args * @throws Exception */public static void main(String[] args) throws Exception {// 建立包含Runtime.getRuntime().availableProcessors()傳回值作為個數的並行線程的ForkJoinPoolForkJoinPool forkJoinPool = new ForkJoinPool();// 提交可分解的PrintTask任務forkJoinPool.submit(new PrintTask(0, 200));forkJoinPool.awaitTermination(2, TimeUnit.SECONDS);//阻塞當前線程直到 ForkJoinPool 中所有的任務都執行結束// 關閉線程池forkJoinPool.shutdown();}}

運行結果如下:

ForkJoinPool-1-worker-2的i值:75ForkJoinPool-1-worker-2的i值:76ForkJoinPool-1-worker-2的i值:77ForkJoinPool-1-worker-2的i值:78ForkJoinPool-1-worker-2的i值:79ForkJoinPool-1-worker-2的i值:80ForkJoinPool-1-worker-2的i值:81ForkJoinPool-1-worker-2的i值:82ForkJoinPool-1-worker-2的i值:83ForkJoinPool-1-worker-2的i值:84ForkJoinPool-1-worker-2的i值:85ForkJoinPool-1-worker-2的i值:86ForkJoinPool-1-worker-2的i值:87ForkJoinPool-1-worker-2的i值:88ForkJoinPool-1-worker-2的i值:89ForkJoinPool-1-worker-2的i值:90ForkJoinPool-1-worker-2的i值:91ForkJoinPool-1-worker-2的i值:92ForkJoinPool-1-worker-2的i值:93ForkJoinPool-1-worker-2的i值:94ForkJoinPool-1-worker-2的i值:95ForkJoinPool-1-worker-2的i值:96ForkJoinPool-1-worker-2的i值:97ForkJoinPool-1-worker-2的i值:98ForkJoinPool-1-worker-2的i值:99ForkJoinPool-1-worker-2的i值:50ForkJoinPool-1-worker-2的i值:51ForkJoinPool-1-worker-2的i值:52ForkJoinPool-1-worker-2的i值:53ForkJoinPool-1-worker-2的i值:54ForkJoinPool-1-worker-2的i值:55ForkJoinPool-1-worker-2的i值:56ForkJoinPool-1-worker-2的i值:57ForkJoinPool-1-worker-2的i值:58ForkJoinPool-1-worker-2的i值:59ForkJoinPool-1-worker-2的i值:60ForkJoinPool-1-worker-2的i值:61ForkJoinPool-1-worker-2的i值:62ForkJoinPool-1-worker-2的i值:63ForkJoinPool-1-worker-2的i值:64ForkJoinPool-1-worker-2的i值:65ForkJoinPool-1-worker-2的i值:66ForkJoinPool-1-worker-2的i值:67ForkJoinPool-1-worker-2的i值:68ForkJoinPool-1-worker-2的i值:69ForkJoinPool-1-worker-1的i值:175ForkJoinPool-1-worker-1的i值:176ForkJoinPool-1-worker-1的i值:177ForkJoinPool-1-worker-1的i值:178ForkJoinPool-1-worker-1的i值:179ForkJoinPool-1-worker-1的i值:180ForkJoinPool-1-worker-1的i值:181ForkJoinPool-1-worker-1的i值:182ForkJoinPool-1-worker-1的i值:183ForkJoinPool-1-worker-1的i值:184ForkJoinPool-1-worker-1的i值:185ForkJoinPool-1-worker-1的i值:186ForkJoinPool-1-worker-1的i值:187ForkJoinPool-1-worker-1的i值:188ForkJoinPool-1-worker-1的i值:189ForkJoinPool-1-worker-1的i值:190ForkJoinPool-1-worker-1的i值:191ForkJoinPool-1-worker-1的i值:192ForkJoinPool-1-worker-1的i值:193ForkJoinPool-1-worker-1的i值:194ForkJoinPool-1-worker-1的i值:195ForkJoinPool-1-worker-1的i值:196ForkJoinPool-1-worker-1的i值:197ForkJoinPool-1-worker-1的i值:198ForkJoinPool-1-worker-1的i值:199ForkJoinPool-1-worker-1的i值:150ForkJoinPool-1-worker-1的i值:151ForkJoinPool-1-worker-1的i值:152ForkJoinPool-1-worker-1的i值:153ForkJoinPool-1-worker-1的i值:154ForkJoinPool-1-worker-1的i值:155ForkJoinPool-1-worker-1的i值:156ForkJoinPool-1-worker-1的i值:157ForkJoinPool-1-worker-1的i值:158ForkJoinPool-1-worker-1的i值:159ForkJoinPool-1-worker-1的i值:160ForkJoinPool-1-worker-1的i值:161ForkJoinPool-1-worker-1的i值:162ForkJoinPool-1-worker-1的i值:163ForkJoinPool-1-worker-1的i值:164ForkJoinPool-1-worker-1的i值:165ForkJoinPool-1-worker-1的i值:166ForkJoinPool-1-worker-1的i值:167ForkJoinPool-1-worker-1的i值:168ForkJoinPool-1-worker-1的i值:169ForkJoinPool-1-worker-1的i值:170ForkJoinPool-1-worker-1的i值:171ForkJoinPool-1-worker-1的i值:172ForkJoinPool-1-worker-1的i值:173ForkJoinPool-1-worker-1的i值:174ForkJoinPool-1-worker-1的i值:125ForkJoinPool-1-worker-1的i值:126ForkJoinPool-1-worker-1的i值:127ForkJoinPool-1-worker-1的i值:128ForkJoinPool-1-worker-1的i值:129ForkJoinPool-1-worker-1的i值:130ForkJoinPool-1-worker-1的i值:131ForkJoinPool-1-worker-1的i值:132ForkJoinPool-1-worker-1的i值:133ForkJoinPool-1-worker-1的i值:134ForkJoinPool-1-worker-1的i值:135ForkJoinPool-1-worker-1的i值:136ForkJoinPool-1-worker-1的i值:137ForkJoinPool-1-worker-1的i值:138ForkJoinPool-1-worker-1的i值:139ForkJoinPool-1-worker-1的i值:140ForkJoinPool-1-worker-1的i值:141ForkJoinPool-1-worker-1的i值:142ForkJoinPool-1-worker-1的i值:143ForkJoinPool-1-worker-1的i值:144ForkJoinPool-1-worker-1的i值:145ForkJoinPool-1-worker-1的i值:146ForkJoinPool-1-worker-1的i值:147ForkJoinPool-1-worker-1的i值:148ForkJoinPool-1-worker-1的i值:149ForkJoinPool-1-worker-1的i值:100ForkJoinPool-1-worker-1的i值:101ForkJoinPool-1-worker-1的i值:102ForkJoinPool-1-worker-1的i值:103ForkJoinPool-1-worker-1的i值:104ForkJoinPool-1-worker-1的i值:105ForkJoinPool-1-worker-1的i值:106ForkJoinPool-1-worker-1的i值:107ForkJoinPool-1-worker-1的i值:108ForkJoinPool-1-worker-1的i值:109ForkJoinPool-1-worker-1的i值:110ForkJoinPool-1-worker-1的i值:111ForkJoinPool-1-worker-1的i值:112ForkJoinPool-1-worker-1的i值:113ForkJoinPool-1-worker-1的i值:114ForkJoinPool-1-worker-1的i值:115ForkJoinPool-1-worker-1的i值:116ForkJoinPool-1-worker-1的i值:117ForkJoinPool-1-worker-1的i值:118ForkJoinPool-1-worker-1的i值:119ForkJoinPool-1-worker-1的i值:120ForkJoinPool-1-worker-1的i值:121ForkJoinPool-1-worker-1的i值:122ForkJoinPool-1-worker-1的i值:123ForkJoinPool-1-worker-1的i值:124ForkJoinPool-1-worker-1的i值:25ForkJoinPool-1-worker-1的i值:26ForkJoinPool-1-worker-1的i值:27ForkJoinPool-1-worker-1的i值:28ForkJoinPool-1-worker-1的i值:29ForkJoinPool-1-worker-1的i值:30ForkJoinPool-1-worker-1的i值:31ForkJoinPool-1-worker-1的i值:32ForkJoinPool-1-worker-1的i值:33ForkJoinPool-1-worker-1的i值:34ForkJoinPool-1-worker-1的i值:35ForkJoinPool-1-worker-1的i值:36ForkJoinPool-1-worker-1的i值:37ForkJoinPool-1-worker-1的i值:38ForkJoinPool-1-worker-1的i值:39ForkJoinPool-1-worker-1的i值:40ForkJoinPool-1-worker-1的i值:41ForkJoinPool-1-worker-1的i值:42ForkJoinPool-1-worker-1的i值:43ForkJoinPool-1-worker-1的i值:44ForkJoinPool-1-worker-1的i值:45ForkJoinPool-1-worker-1的i值:46ForkJoinPool-1-worker-1的i值:47ForkJoinPool-1-worker-1的i值:48ForkJoinPool-1-worker-1的i值:49ForkJoinPool-1-worker-1的i值:0ForkJoinPool-1-worker-1的i值:1ForkJoinPool-1-worker-1的i值:2ForkJoinPool-1-worker-1的i值:3ForkJoinPool-1-worker-1的i值:4ForkJoinPool-1-worker-1的i值:5ForkJoinPool-1-worker-1的i值:6ForkJoinPool-1-worker-1的i值:7ForkJoinPool-1-worker-1的i值:8ForkJoinPool-1-worker-1的i值:9ForkJoinPool-1-worker-1的i值:10ForkJoinPool-1-worker-1的i值:11ForkJoinPool-1-worker-1的i值:12ForkJoinPool-1-worker-1的i值:13ForkJoinPool-1-worker-1的i值:14ForkJoinPool-1-worker-1的i值:15ForkJoinPool-1-worker-1的i值:16ForkJoinPool-1-worker-1的i值:17ForkJoinPool-1-worker-1的i值:18ForkJoinPool-1-worker-1的i值:19ForkJoinPool-1-worker-1的i值:20ForkJoinPool-1-worker-1的i值:21ForkJoinPool-1-worker-1的i值:22ForkJoinPool-1-worker-1的i值:23ForkJoinPool-1-worker-1的i值:24ForkJoinPool-1-worker-2的i值:70ForkJoinPool-1-worker-2的i值:71ForkJoinPool-1-worker-2的i值:72ForkJoinPool-1-worker-2的i值:73ForkJoinPool-1-worker-2的i值:74

從上面結果來看,ForkJoinPool啟動了兩個線程來執行這個列印任務,這是因為筆者的電腦的CPU是雙核的。不僅如此,讀者可以看到程式雖然列印了0-199這兩百個數字,但是並不是連續列印的,這是因為程式將這個列印任務進行了分解,分解後的任務會並存執行,所以不會按順序從0列印 到199。


二、RecursiveTask

下面以一個有返回值的大任務為例,介紹一下RecursiveTask的用法。

大任務是:計算隨機的100個數位和。

小任務是:每次只能20個數值的和。

import java.util.Random;import java.util.concurrent.ForkJoinPool;import java.util.concurrent.Future;import java.util.concurrent.RecursiveTask;//RecursiveTask為ForkJoinTask的抽象子類,有傳回值的任務class SumTask extends RecursiveTask<Integer> {// 每個"小任務"最多隻列印50個數private static final int MAX = 20;private int arr[];private int start;private int end;SumTask(int arr[], int start, int end) {this.arr = arr;this.start = start;this.end = end;}@Overrideprotected Integer compute() {int sum = 0;// 當end-start的值小於MAX時候,開始列印if ((end - start) < MAX) {for (int i = start; i < end; i++) {sum += arr[i];}return sum;} else {System.err.println("=====任務分解======");// 將大任務分解成兩個小任務int middle = (start + end) / 2;SumTask left = new SumTask(arr, start, middle);SumTask right = new SumTask(arr, middle, end);// 並存執行兩個小任務left.fork();right.fork();// 把兩個小任務累加的結果合并起來return left.join() + right.join();}}}public class ForkJoinPoolTest2 {/** * @param args * @throws Exception */public static void main(String[] args) throws Exception {int arr[] = new int[100];Random random = new Random();int total = 0;// 初始化100個數字元素for (int i = 0; i < arr.length; i++) {int temp = random.nextInt(100);// 對數組元素賦值,並將數組元素的值添加到total總和中total += (arr[i] = temp);}System.out.println("初始化時的總和=" + total);// 建立包含Runtime.getRuntime().availableProcessors()傳回值作為個數的並行線程的ForkJoinPoolForkJoinPool forkJoinPool = new ForkJoinPool();// 提交可分解的PrintTask任務Future<Integer> future = forkJoinPool.submit(new SumTask(arr, 0,arr.length));System.out.println("計算出來的總和=" + future.get());// 關閉線程池forkJoinPool.shutdown();}}

計算結果如下:
初始化時的總和=4283=====任務分解===========任務分解===========任務分解===========任務分解===========任務分解===========任務分解===========任務分解======計算出來的總和=4283

從上面結果來看,ForkJoinPool將任務分解了7次,程式通過SumTask計算出來的結果,和初始化數組時統計出來的總和是相等的,這表明計算結果一切正常。



讀者還參考以下文章加深對ForkJoinPool的理解:

http://www.infoq.com/cn/articles/fork-join-introduction/

http://www.ibm.com/developerworks/cn/java/j-lo-forkjoin/


==================================================================================================

  歐陽鵬  歡迎轉載,與人分享是進步的源泉!

  轉載請保留原文地址:http://blog.csdn.net/ouyang_peng

==================================================================================================




我的Java開發學習之旅------>Java使用Fork/Join架構來並存執行任務

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.