前言
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,阐明它也是一种线程池。
再来看看要害属性:
今天再剖析,先去跟妹子聊天了,嘻嘻~