通俗易懂的JUC源碼剖析-ForkJoinPool

前言

ForkJoinPool經常使用於將大任務分解(Fork)成若干小任務並行執行,而後再把每一個小任務的執行結果合併起來(Join)獲得大任務的最終結果。下面是示意圖(ps:盜網上網上盜的圖,禁止套娃!)
image.png
ForkJoinPool一般配合ForkJoinTask一塊兒使用,ForkJoinTask表明一個任務,它是個抽象類,它的常見子類有RecursiveTask和RecursiveAction,其中RecursiveTask有返回值,RecursiveAction無返回值。java

下面舉個簡單栗子來講明ForkJoinPool的使用場景。
場景:計算整數1~10000000的和。ide

最傳統的方式就是直接for循環累加,但這裏咱們也能夠用ForkJoinPool實現並行計算,以此提高性能(數據量很大時)。代碼以下:oop

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();
            }
        }
    }
}

輸出結果:
image.png性能

結果很奇怪,傳統for循環耗時反而更短,這是由於10000000數據量還不夠大,沒有發揮ForkJoinPool的優點,而且因爲fork和join的操做反而消耗了性能。咱們再加個0看看效果。this

image.png
能夠看到,這時候使用ForkJoinPool性能就提高了。spa

實現原理

先看看類圖結構:線程

@sun.misc.Contended
public class ForkJoinPool extends AbstractExecutorService {
}

它跟ThreadPoolExecutor同樣,也繼承了AbstractExecutorService,說明它也是一種線程池。code

再來看看關鍵屬性:blog

明天再分析,先去跟妹子聊天了,嘻嘻~繼承

相關文章
相關標籤/搜索