一、任務分解問題和ForkJoinPool簡介
在多線程併發編程中,有時候會遇到將大任務分解成小任務再併發執行的場景。Java 8新增的ForkJoinPool很好的支持了這個問題。
ForkJoinPool是一種支持任務分解的線程池,當提交給他的任務“過大”,他就會按照預先定義的規則將大任務分解成小任務,多線程併發執行。
一般要配合可分解任務接口ForkJoinTask來使用,ForkJoinTask有兩個實現它的抽象類:RecursiveAction和RecursiveTask,其區別是前者沒有返回值,後者有返回值。
下面通過具體代碼,來示範兩個問題:(1)怎麼定義可分解的任務類 (2)如何使用ForkJoinPool
package com;
import java.util.Random;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;
import java.util.concurrent.TimeUnit;
public class Main {
/**定義一個可分解的的任務類,繼承了RecursiveAction抽象類
* 必須實現它的compute方法
*/
public static class myTask extends RecursiveAction {
private static final long serialVersionUID = 1L;
//定義一個分解任務的閾值——50,即一個任務最多承擔50個工作量
int THRESHOLD=50;
//任務量
int task_Num=0;
myTask(int Num){
this.task_Num=Num;
}
@Override
protected void compute() {
if(task_Num<=THRESHOLD){
System.out.println(Thread.currentThread().getName()+"承擔了"+task_Num+"份工作");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
//隨機解成兩個任務
Random m=new Random();
int x=m.nextInt(50);
myTask left=new myTask(x);
myTask right=new myTask(task_Num-x);
left.fork();
right.fork();
}
}
}
public static void main(String[] args) throws Exception {
//創建一個支持分解任務的線程池ForkJoinPool
ForkJoinPool pool=new ForkJoinPool();
myTask task=new myTask(178);
pool.submit(task);
pool.awaitTermination(20, TimeUnit.SECONDS);//等待20s,觀察結果
pool.shutdown();
}
}
運行結果如下:
ForkJoinPool-1-worker-1承擔了34份工作
ForkJoinPool-1-worker-2承擔了34份工作
ForkJoinPool-1-worker-3承擔了20份工作
ForkJoinPool-1-worker-0承擔了14份工作
ForkJoinPool-1-worker-0承擔了48份工作
ForkJoinPool-1-worker-3承擔了7份工作
ForkJoinPool-1-worker-2承擔了21份工作
二、總結
通過運行結果可以發現,ForkJoinPool支持開啓新線程執行被分解的任務,同時也會複用以前的老線程去承擔被分解的任務,具備線程池的通用屬性。
更多線程池的資料,請參考本人的博文: Java 中7種線程池詳解+示例代碼