前言
ForkJoinPool常用于将大任务分解(Fork)成若干小任务并行执行,然后再把每个小任务的执行结果合并起来(Join)得到大任务的最终结果。下面是示意图(ps:盗网上网上盗的图,禁止套娃!)
ForkJoinPool通常配合ForkJoinTask一起使用,ForkJoinTask代表一个任务,它是个抽象类,它的常见子类有RecursiveTask和RecursiveAction,其中RecursiveTask有返回值,RecursiveAction无返回值。
下面举个简单栗子来说明ForkJoinPool的使用场景。
场景:计算整数1~10000000的和。
最传统的方式就是直接for循环累加,但这里我们也可以用ForkJoinPool实现并行计算,以此提升性能(数据量很大时)。代码如下:
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
import java.util.stream.LongStream;
public class ForkJoinPoolDemo {
public static void main(String[] args) {
long[] nums = LongStream.rangeClosed(1, 10000000).toArray();
ForkJoinPool pool = new ForkJoinPool();
Instant before = Instant.now();
Long result = pool.invoke(new ComputeSumTask(nums, 0, nums.length - 1));
Instant after = Instant.now();
pool.shutdown();
System.out.println(result);
System.out.println("cost time(ms) : " + Duration.between(before, after).toMillis());
System.out.println("Now let's watch traditional for-loop cost time below :");
before = Instant.now();
result = forLoopCompute(nums);
after = Instant.now();
System.out.println(result);
System.out.println("cost time(ms) : " + Duration.between(before, after).toMillis());
}
private static Long forLoopCompute(long[] nums) {
long sum = 0;
for (long num : nums) {
sum += num;
}
return sum;
}
static class ComputeSumTask extends RecursiveTask<Long> {
private int start;
private int end;
private long[] nums;
ComputeSumTask(long[] nums, int start, int end) {
this.nums = nums;
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
// 当需要计算的数字个数小于10000时,退化成直接for循环计算
// 注意这里的阈值要合适,太小的话容易导致内存溢出,太大的话发挥不了ForkJoinPool的优势。
if (end - start < 10000) {
long sum = 0;
for (int i = start; i <= end; i++) {
sum += nums[i];
}
return sum;
} else {
int mid = (end - start) / 2 + start;
ComputeSumTask leftTask = new ComputeSumTask(nums, start, mid);
ComputeSumTask rightTask = new ComputeSumTask(nums, mid + 1, end);
leftTask.fork();
rightTask.fork();
return leftTask.join() + rightTask.join();
}
}
}
}
输出结果:
结果很奇怪,传统for循环耗时反而更短,这是因为10000000数据量还不够大,没有发挥ForkJoinPool的优势,并且由于fork和join的操作反而消耗了性能。我们再加个0看看效果。
可以看到,这时候使用ForkJoinPool性能就提升了。
实现原理
先看看类图结构:
@sun.misc.Contended
public class ForkJoinPool extends AbstractExecutorService {
}
它跟ThreadPoolExecutor一样,也继承了AbstractExecutorService,说明它也是一种线程池。
再来看看关键属性:
明天再分析,先去跟妹子聊天了,嘻嘻~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。