java 併發計算框架 forkjoin 使用

1.介紹

Fork/Join爲JKD1.7引入,適用於對大量數據進行拆分成多個小任務進行計算的框架,

最後把所有小任務的結果彙總合併得到最終的結果

相關類

public abstract class RecursiveTask<V> extends ForkJoinTask<V>;
public abstract class RecursiveAction extends ForkJoinTask<Void>;

其中RecursiveTask在執行有返回值的任務時使用,RecursiveAction在執行沒有返回值的任務時使用

2.實例代碼

ForkJoin有參無返回值

public class CountRecursiveTask extends RecursiveAction {
    // 達到子任務直接計算的閾值
    private int num = 15;

    private int start;
    private int end;

    public CountRecursiveTask(int start, int end) {
        this.start = start;
        this.end = end;
    }

    @Override
    protected void compute() {
        if (this.end - this.start < num) {
            System.out.println("start:" + start + ";end:" + end);
        } else {
            //fork 2 tasks:Th = 15
            //如果仍大於閾值,則繼續拆分爲2個子任務,分別調用fork方法。
            //這裏可以根據情況拆成n個子任務
            int middle = (end + start) / 2;
            CountRecursiveTask left = new CountRecursiveTask(start, middle);
            left.fork();
            System.out.println("start:" + start + ";middle:" + middle + ";end:" + end);
            CountRecursiveTask right = new CountRecursiveTask(middle + 1, end);
            right.fork();

        }
    }

    public static void main(String[] args){
        ForkJoinPool forkJoinPool = new ForkJoinPool(Runtime.getRuntime().availableProcessors());

        forkJoinPool.invoke(new CountRecursiveTask(1, 100));
    }

}

ForkJoin有參有返回值(繼承RecursiveTask<T>類)

public class CountRecursiveTask extends RecursiveTask<Integer> {
    // 達到子任務直接計算的閾值
    private int num = 15;

    private int start;
    private int end;

    public CountRecursiveTask(int start, int end) {
        this.start = start;
        this.end = end;
    }

    @Override
    protected Integer compute() {
        if (this.end - this.start < num) {
            //如果小於閾值,直接調用最小任務的計算方法
            return count();
        } else {
            //fork 2 tasks:Th = 15
            //如果仍大於閾值,則繼續拆分爲2個子任務,分別調用fork方法。
            //這裏可以根據情況拆成n個子任務
            int middle = (end + start) / 2;
            CountRecursiveTask left = new CountRecursiveTask(start, middle);
            System.out.println("start:" + start + ";middle:" + middle + ";end:" + end);
            left.fork();
            CountRecursiveTask right = new CountRecursiveTask(middle + 1, end);
            right.fork();
            //最後一定要記得fork()結果(如果需要結果的話)
            return left.join() + right.join();
        }
    }

    private int count() {
        int sum = 0;
        for (int i = start; i <= end; i++) {
            sum += i;
        }
        return sum;
    }

    public static void main(String[] args){
        ForkJoinPool forkJoinPool = new ForkJoinPool(Runtime.getRuntime().availableProcessors());

        Integer sum = forkJoinPool.invoke(new CountRecursiveTask(1, 100));
        System.out.println(sum);
    }

}

在有大量計算任務時,此框架方法可進行並行計算效率高,

以上示例,可以根據具體的業務需求更改屬性及相關方法用於匹配自己的業務邏輯

JDK1.8後由於加入Stream流的操作,集合框架可以使用

Collection<E> default Stream<E> parallelStream()的方法轉換成並行流進行計算,此時效果與Fork/Join任務同效

ForkJoinPool中的多種方法

public <T> ForkJoinTask<T> submit(ForkJoinTask<T> task);//等待獲取結果
public void execute(ForkJoinTask<?> task);//異步執行
public <T> T invoke(ForkJoinTask<T> task);//執行,獲取Future

ForkJoinTask在執行的時候可能會拋出異常,但是沒辦法在主線程裏直接捕獲異常,

所以ForkJoinTask提供了isCompletedAbnormally()方法來檢查任務是否已經拋出異常或已經被取消了,

並且可以通過ForkJoinTask的getException方法獲取異常。

getException方法返回Throwable對象,如果任務被取消了則返回CancellationException。

如果任務沒有完成或者沒有拋出異常則返回null。

if(task.isCompletedAbnormally()) {
    System.out.println(task.getException());
}

 

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