14、分支合併:ForkJoin
14.1. 什麼是ForkJoin
ForkJoin,任務切分、合併操作。大數據中的mapreduce ,就是任務切分,結果合併。原理如下圖所示:
14.2. 工作竊取
工作竊取
A 任務1 --> 任務2 --> 任務3 --> 任務4 A領先執行完成 ,幫B執行任務(從B任務的尾部開始竊取任務執行)!
B 任務1 --> 任務2 任務3 任務4
工作開始從頭,竊取從尾,有效提高速度,雙端隊列
14.3. 核心類
14.3.1. ForkJoinPool
1、ForkJoinPool
ForkJoinPool ,是實現了ExecutorService的任務池,將任務ForkJoinTask放到ForkJoinPool中,去運行線程。 通過隊列來執行,找到實現接口的類
而ForkJoinPool 中存在一個內部類,工作隊列WorkQueue,是ForkJoinPool 的 一個內部類。每一個線程都有一個 WorkQueue !
14.3.2. ForkJoinTask
2、ForkJoinTask
ForkJoinTask 是一個抽象類,代表正在 ForkJoinPool 中運行的 任務,它有三個主要的方法:
fork: 安排任務異步執行,簡單的說,就是創建一個子任務。
join:當任務完成後獲取去返回的計算結果!
invoke:開始執行!如果計算沒有完畢,就會等待!
14.3.3. RecursiveTask
3、RecursiveTask
**ForkJoinTask的一個重要子類:**遞歸 RecursiveTask
其中有個計算方法compute
/**
* The main computation performed by this task.
* @return the result of the computation
*/
protected abstract V compute();
我們一般要繼承RecursiveTask類,重寫compute方法,如下示例:
MyRecursiveTask
package com.interview.concurrent.stream;
import java.util.concurrent.RecursiveTask;
/**
* @author yangxj
* @description 描述:遞歸任務,泛型是計算後返回的結果類型
* @date 2020/2/24 15:56
*/
public class MyRecursiveTask extends RecursiveTask<Long> {
private long start; //開始值
private long end; //結束值
private static final long temp = 10000L; //中間值
public MyRecursiveTask(long start, long end) {
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
if (end - start <= temp) {
long sum = 0L;
for (long i = start; i <= end; i++) {
sum += i;
}
return sum;
}else{
//獲取中間值
long middle = (start + end)/2;
/**
* fork()會不斷的循環
*/
//第一個任務
MyRecursiveTask rightTask = new MyRecursiveTask(start,middle);
rightTask.fork();
//第二個任務
MyRecursiveTask leftTask = new MyRecursiveTask(middle+1, end);
leftTask.fork();
//合併結果
return rightTask.join() + leftTask.join();
}
}
}
fork()會不斷的循環。
ForkJoin代碼編寫模型:
1、創建ForkJoinPool;
2、創建ForkJoinTask;
3、ForkJoinPool對象調用invoke執行,並將ForkJoinTask對象放入ForkJoinPool中
示例:計算1到10,0000,0000的和,通過三種方式,比較性能
package com.interview.concurrent.stream;
import java.util.concurrent.ForkJoinPool;
import java.util.stream.LongStream;
/**
* @author yangxj
* @description 描述:計算1到10,0000,0000的和
* @date 2020/2/24 15:40
*/
public class ForkJoinDemo {
public static void main(String[] args) {
//calculateNormal(); //time:781 sum:500000000500000000
//calculateForkJoin(); //time:724 sum:500000000500000000
calculateStream(); //time:473 sum:500000000500000000
}
// 正常測試
public static void calculateNormal(){
long startTime = System.currentTimeMillis();
long sum = 0L;
for (long i = 0L; i <= 10_0000_0000L; i++) {
sum += i;
}
long endTime = System.currentTimeMillis();
System.out.println("time:"+(endTime-startTime)+" sum:"+sum);
}
// ForkJoin測試
public static void calculateForkJoin(){
long startTime = System.currentTimeMillis();
/**
*1、創建ForkJoinPool;
*/
ForkJoinPool forkJoinPool = new ForkJoinPool();
/**
*2、創建ForkJoinTask;
*/
MyRecursiveTask recursiveTask = new MyRecursiveTask(0L,10_0000_0000L);
/**
3、ForkJoinPool對象調用invoke執行,並將ForkJoinTask對象放入ForkJoinPool中;
*/
long sum = forkJoinPool.invoke(recursiveTask);
long endTime = System.currentTimeMillis();
System.out.println("time:"+(endTime-startTime)+" sum:"+sum);
}
// Stream並行流測試
public static void calculateStream(){
long startTime = System.currentTimeMillis();
long sum = LongStream.rangeClosed(0L,10_0000_0000L).parallel().reduce(0L,Long::sum);
long endTime = System.currentTimeMillis();
System.out.println("time:"+(endTime-startTime)+" sum:"+sum);
}
}