Fork/Join
在JDK7之後,Java加入了並行計算的框架Fork/Join,來解決系統中大數據計算的性能問題。在這裏要強調的是並行並不是併發,並行是指系統內有多個任務同時執行,併發是指系統內有多個任務同時存在。不同的任務按時間分片的方式切換執行,由於切換的時間很短,給人的感覺好像是在同時執行。
Fork/Join採用的是分治法,Fork是講一個大任務拆分成若干個子任務,子任務分別去計算,而Join是獲取子任務的計算結果,然後合併。就像分佈式裏面的MapReduce一樣。Java的Fork/Join是一個遞歸的過程,子任務被分配到不同的核上執行,效率最高。
ForkJoinPool
Fork/Join框架的核心類是ForkJoinPool,ForkJoinPool是ExecutorService的一個實例,它能夠接收一個ForkJoinTask,並得到計算結果。ForkJoinTask有兩個子類, RecursiveAction(無返回結果), RecursiveTask(有返回結果)。定義任務時,只要繼承這兩個類就可。
下面是一個實例,計算一個超大數組所有元素的和。
public class ForkJoinPoolTest extends RecursiveTask<Integer>{
private static final long serialVersionUID = 1L;
private static final int THRESHOLD = 100;//閾值
private long[] array;
private int low;
private int high;
public ForkJoinPoolTest(long[] array, int low, int high) {
this.array = array;
this.low = low;
this.high = high;
}
@Override
protected Integer compute() {
int sum = 0;
if(high - low <= THRESHOLD){
//小於閾值這直接計算
for(int i=low;i<high;i++){
sum += array[i];
}
} else{
//將一個大任務分成兩個小任務
int mid = (low + high) >>> 1;
ForkJoinPoolTest left = new ForkJoinPoolTest(array, low, mid);
ForkJoinPoolTest right = new ForkJoinPoolTest(array, mid+1, high);
//分別計算
left.fork();
right.fork();
sum = left.join() + right.join();
}
return sum;
}
public static void main(String[] args) throws InterruptedException, ExecutionException{
long[] array = geeArray(50000);
System.out.println(Arrays.toString(array));
//創建任務
ForkJoinPoolTest task = new ForkJoinPoolTest(array, 0, array.length - 1);
long begin = System.currentTimeMillis();
//創建線程池
ForkJoinPool forkJoinPool = new ForkJoinPool();
//提交任務到線程池
forkJoinPool.submit(task);
//獲取結果
Integer result = task.get();
long end = System.currentTimeMillis();
System.out.println(String.format("結果 %s 耗時 %s ms", result, end - begin));
}
private static long[] geeArray(int size){
long[] array = new long[size];
for(int i=0;i<array.length;i++)
array[i] = new Random().nextLong();
return array;
}
}
通過調整閾值(THRESHOLD),可以發現耗時是不一樣的。