併發編程系列之什麼是ForkJoin框架?

併發編程系列之什麼是ForkJoin框架?

1、什麼是ForkJoin框架

ForkJoin框架是java的JUC包裏提供的,用於處理一些比較繁重的任務,會將這個大任務分爲多個小任務,多個小任務處理完成後會將結果彙總給Result,體現的是一種“分而治之”的思想。第一步,拆分fork任務,將大任務分爲多個小任務;第二步,歸併join,會將小任務的處理結果進行歸併爲一個結果。


2、ForkJoinTask

ForkJoinTaskForkJoin框架的提供的任務API,ForkJoinTask是一個抽象類,有兩個主要的實現類,RecursiveTaskRecursiveAction,其中RecursiveTaskRecursiveAction的主要區別是,RecursiveAction沒有返回值,而RecursiveTask是有返回值的

3、ForkJoinPool

ForkJoinPool類是forkjoin框架的線程池實現,基於ExecutorService接口。這個線程池是jdk1.7才加入的,用於管理線程,執行forkjoin的任務。對於線程池的使用,我們使用ThreadPoolExecutor比較多,可以在idea裏看一下uml類圖,可以看出ForkJoinPool和ThreadPoolExecutor實現差不多的。


ForkJoinPool()
ForkJoinPool(int parallelism)
ForkJoinPool(int parallelism, ForkJoinWorkerThreadFactory factory,
 UncaughtExceptionHandler handler, boolean asyncMode)

幾個重要的參數:

  • parallelism:並行度,並行執行線程,可用指定,也可以不指定,不指定的情況,是根據cpu核數創建可用的線程
  • ForkJoinWorkerThreadFactory:創建線程的工廠實現
  • UncaughtExceptionHandler :因爲未知異常中斷的回調處理
  • asyncMode:是否異步,默認情況是false

使用時候,可以直接創建ForkJoinPool,可以不傳參,不傳參的情況,默認指定的線程並行數爲Runtime.getRunTime().availableProcessors();,表示根據cpu核數創建可用線程數

ForkJoinPool forkJoinPool = new ForkJoinPool();
ArraySortTask task = new ArraySortTask(array , 0 , size);
forkJoinPool.submit(task);
task.get();

也是可用傳參,對並行度進行指定的public ForkJoinPool(int parallelism), parallelism並行度,並行執行幾個線程

將forkjoin任務加入到FrokJoinPool線程池有幾種方式

  • execute():調用其 fork 方法在多個線程之間拆分工作。
  • invoke():在ForkJoinPool線程池上調用invoke方法
  • submit():返回一個Future對象,Future可以進行監控,任務完成後返回結果

4、打印斐波那契數列

ForkJoin框架可以用於一些遞歸遍歷的場景,對於斐波那契數列,你可以比較熟悉,因爲在面試中有時候經常問到,斐波那契數列的特點就是最後一項的結果等於前面兩項的和

package com.example.concurrent.forkjoin;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;

/**
 * <pre>
 *      斐波那契數列
 * </pre>
 * <p>
 * <pre>
 * @author nicky.ma
 * 修改記錄
 *    修改後版本:     修改人:  修改日期: 2021/10/12 16:22  修改內容:
 * </pre>
 */
public class Fibonacci extends RecursiveTask<Integer>{

    private int n;

    public Fibonacci(int n) {
        this.n = n;
    }

    @Override
    protected Integer compute() {
        if (n <= 1)
            return n;
        Fibonacci f1 = new Fibonacci(n - 1);
        f1.fork();
        Fibonacci f2 = new Fibonacci(n - 2);
        f2.fork();
        return f1.join() + f2.join();
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ForkJoinPool pool = new ForkJoinPool();
        for (int i = 0; i< 10; i++) {
            ForkJoinTask task = pool.submit(new Fibonacci(i));
            System.out.println(task.get());
        }
    }
}

5、ForkJoin歸併排序

面試題:快速實現對一個長度百萬的數組的排序

難點:可以使用歸併排序,多線程如何組織實現歸併排序

package com.example.concurrent.forkjoin;

import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;

/**
 * <pre>
 *      大數組排序
 * </pre>
 * <p>
 * <pre>
 * @author mazq
 * 修改記錄
 *    修改後版本:     修改人:  修改日期: 2021/10/12 17:04  修改內容:
 * </pre>
 */
public class ArraySortTask extends RecursiveAction{

    final long[] array; final int lo, hi;
    ArraySortTask(long[] array, int lo, int hi) {
        this.array = array; this.lo = lo; this.hi = hi;
    }
    ArraySortTask(long[] array) { this(array, 0, array.length); }

    @Override
    protected void compute() {
        if (hi - lo < THRESHOLD)
            // 少於閥值,使用Arrays.sort 快排
            sortSequentially(lo, hi);
        else {
            /* 歸併排序 */
            // 取中間值
            int mid = (lo + hi) >>> 1;
            // 拆分任務
            invokeAll(new ArraySortTask(array, lo, mid),
                    new ArraySortTask(array, mid, hi));
            // 歸併結果
            merge(lo, mid, hi);
        }
    }

    // implementation details follow:
    static final int THRESHOLD = 1000;
    void sortSequentially(int lo, int hi) {
        Arrays.sort(array, lo, hi);
    }

    void merge(int lo, int mid, int hi) {
        long[] buf = Arrays.copyOfRange(array, lo, mid);
        for (int i = 0, j = lo, k = mid; i < buf.length; j++)
            array[j] = (k == hi || buf[i] < array[k]) ?
                    buf[i++] : array[k++];
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        int size = 10_000;
        long[] array = new long[size];
        Random random = new Random();
        for (int i = 0; i< size; i++) {
            array[i] = random.nextInt();
        }

        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ArraySortTask task = new ArraySortTask(array , 0 , size);
        forkJoinPool.submit(task);
        task.get();

        for (long a : array) {
            System.out.println(a);
        }
    }
}

參考資料

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章