1、ForkJoin 简介
ForkJoin 框架是在 JDK7 之后提供的一种基于分治策略的多线程框架。与分支策略思想一样,即一个大问题可以被拆分为若干个小问题,而且小问题之间互不干扰,并且解法与原问题形式相同,最终将子问题的解合并可以得到原问题的解。
在 ForkJoin 框架中所有被拆分的任务都会进入一个任务队列中,然后由各个线程从任务队列取出对应的任务执行,由于每个任务的执行时间不一定相同,为了更加充分的利用线程, ForkJoin 采用了一种工作密取的方式来处理。
工作密取: 假设有A,B 两个线程。两个线程供一个工作队列,当 B 执行完成后如果B 还没有执行完的话,B 会从队列尾部取出一个任务继续执行。
2、ForkJoinPool 和 ForkJoinTask
java.util.concurrent.ForkJoinPool
实现了ExecutorService
接口:
ForkJoinPool
是ExecutorService
的一个实现类,因此它也是一个线程池。它支持将一个任务拆分成多个“小任务”并行计算,再把多个“小任务”的结果合并成总的计算结果。
ForkJoinPool
类比为线程池,那么ForkJoinTask
就可以类比为线程。
ForkJoinTask
有两个常用的实现类:
3、测试用例
测试计算:从 1 加到 9 亿所需耗时。
3.1 普通 for 循环
代码:
package pers.klb.myjuc;
public class ForkJoinTest {
public static void main(String[] args) {
Long testNum = 9_000_000_000L;
test01(testNum);
}
private static void test01(Long testNum) {
Long sum = 0L;
long start = System.currentTimeMillis();
for (Long i = 1L; i <= testNum; i++) {
sum += i;
}
long end = System.currentTimeMillis();
System.out.println("sum=" + sum + " 时间:" + (end - start));
}
}
运行结果:
3.2 使用 ForkJoin
首先继承RecursiveTask
,并重写 compute
方法:
class MyForkJoin extends RecursiveTask<Long> {
private long start;
private long end;
private final long threshold = 1_000_000L;
private long result = 0L;
public MyForkJoin(Long start, Long end) {
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
if ((end - start) > threshold) {
for (long i = start; i <= end; i++) {
result += i;
}
} else {
long mid = (start + end) >>> 1;
MyForkJoin myForkJoin01 = new MyForkJoin(start, mid);
MyForkJoin myForkJoin02 = new MyForkJoin(mid + 1, end);
myForkJoin01.fork();
myForkJoin02.fork();
result = myForkJoin01.join() + myForkJoin02.join();
}
return result;
}
}
测试方法:
package pers.klb.myjuc;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
public class ForkJoinTest {
public static void main(String[] args) {
long testNum = 9_000_000_000L;
test02(testNum);
}
private static void test02(long testNum) {
long sum = 0L;
long start = System.currentTimeMillis();
try {
// 创建池
ForkJoinPool forkJoinPool = new ForkJoinPool();
// 创建任务
MyForkJoin myForkJoin = new MyForkJoin(0L, testNum);
// 提交任务并执行
ForkJoinTask<Long> submit = forkJoinPool.submit(myForkJoin);
// 获取结果
sum = submit.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("sum=" + sum + " 时间:" + (end - start));
}
}
运行结果:
3.3 Stream 并行流
测试代码:
public class ForkJoinTest {
public static void main(String[] args) {
long testNum = 9_000_000_000L;
test03(testNum);
}
private static void test03(long testNum){
long start = System.currentTimeMillis();
long reduce = LongStream.rangeClosed(0, testNum).parallel().reduce(0, (x, y) -> x + y);
long end = System.currentTimeMillis();
System.out.println("sum=" + reduce + " 时间:" + (end - start));
}
}
运行结果: