乐趣区

关于java:通俗易懂的JUC源码剖析ForkJoinPool

前言

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,阐明它也是一种线程池。

再来看看要害属性:

今天再剖析,先去跟妹子聊天了,嘻嘻~

退出移动版