JUC之Fork/Join框架

xbhog發表於2022-01-20

Fork/Join框架和非同步

定義:

Fork/Join框架是Java 7提供的一個用於並行執行任務的框架,是一個把大任務分割成若干個小任務,最終彙總每個小任務結果後得到大任務結果的框架。類似於遞迴或者分而治之的思想。

引用《Java併發程式設計的藝術》

Fork就是把一個大任務切分為若干子任務並行的執行,Join就是合併這些子任務的執行結果,最後得到這個大任務的結

果。比如計算1+2+…+10000,可以分割成10個子任務,每個子任務分別對1000個數進行求和,最終彙總這10個子任務的結果

工作竊取演算法 :

工作竊取(work-stealing)演算法是指某個執行緒從其他佇列裡竊取任務來執行。

當大任務需要處理時,我們把其分割成多個子任務,存放在每個佇列中,並且每個執行緒處理不同佇列中的子任務,每當有執行緒(A)提前完成任務了,那麼(A)執行緒會去其他的佇列中竊取任務處理,這是A執行緒與當前的執行緒一起處理同一個佇列。

由此引出為了減少竊取任務執行緒和被竊取任務執行緒之間的競爭,佇列採用雙端佇列。多執行緒處理同佇列的流程是:

被竊取任務執行緒永遠從雙端佇列的頭部拿任務執行,

竊取任務的執行緒(A)永遠從雙端佇列的尾部拿任務執行。

其優缺點:

工作竊取演算法的優點:充分利用執行緒進行平行計算,減少了執行緒間的競爭。
工作竊取演算法的缺點:在某些情況下還是存在競爭,比如雙端佇列裡只有一個任務時。並且該演算法會消耗了更多的系統資源,比如建立多個執行緒和多個雙端佇列。

使用Fork/Join框架

需求是:計算1+2+3+4+......+100的結果

閾值設定為10,希望每個子任務最大執行10個數的相加。

package com.JucPool;

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

class MyTask extends RecursiveTask<Integer>{

    private static final int THRESHOLD = 10; // 閾值
    private int start;
    private int end;
    private int result;

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

    @Override
    protected Integer compute() {
        if((end-start) <= THRESHOLD){
            for(int i = start; i <= end; i++){
                result +=i;
            }
        }else{
            int mid = (start+end)>>1;
            MyTask task1 = new MyTask(start, mid);
            MyTask task2 = new MyTask(mid+1, end);
            //執行子任務
            task1.fork();
            task2.fork();
            //得到最後結果
            result = task1.join()+task2.join();
        }
        return result;
    }
}

public class demo2 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //建立執行緒池
        ForkJoinPool joinPool = new ForkJoinPool();
        //資源
        MyTask myTask = new MyTask(1,100);
        //執行任務
        ForkJoinTask<Integer> submit = joinPool.submit(myTask);
        System.out.println(submit.get());
    }
}

使用ForkJoinTask資源需要繼承RecursiveTask(用於有返回結果的任務)--ForkJoinTask子類。

首先需要實現compute方法,我們在該方法中判斷任務的大小是否小於我們設定的閾值。如果小於閾值,就直接執行任務。如果不足夠小,就必須分割成兩個子任務,每個子任務在呼叫fork方法時,又會進入compute方法,看看當前子任務是否需要繼續分割成子任務,如果不需要繼續分割,則執行當前子任務並返回結果。使用join方法會等待子任務執行完並得到其結果。

相關文章