Fork/Join框架

Fork/Join框架的思想

Fork/Join的思想是一個分而治之的思想的實現,跟MapReduce的思想如出一轍,簡單的說就是加入我們需要處理1000個數據,但是我們並不具備處理1000個數據的能力,那麼我們可以讓一個線程只處理其中的10個,然後分階段處理100次,將100次的結果進行合併,那麼就會得到對最終的1000個數據的處理結果

JDK併發包中的實現

在實際應用中,我們沒有辦法毫無顧忌地使用fork()開啓線程進行處理,假使我們這麼做了,那麼很有可能導致系統開啓過多的線程而嚴重影響性能。所以在JDK中,給出了一個ForkJoinPool的線程池,對於fork()方法並不急着開啓線程,而是提交給ForkJoinPool線程池進行處理,以節省系統資源。
ForkJoinPool的submit方法:

public <T> ForkJoinTask<T> submit(ForkJoinTask<T> task)

這個方法需要一個ForkJoinTask類型的參數,ForkJoinTask就是支持fork()分解和join()等待的任務。ForkJoinTask是一個抽象類,它有兩個子類,他們分別表示沒有返回值的任務和有返回值的任務

RecursiveAction
RecursiveTask<V>

圖解Fork/Join框架:
圖解Fork/Join框架

案例

計算若干個數值的和

public class CountTask extends RecursiveTask<Long>{

    //每次相加的範圍是1000個數
    private static final Long THRESHOLD = 10L;
    private long start;
    private long end;

    public CountTask(long start, long end) {
        super();
        this.start = start;
        this.end = end;
    }

    @Override
    protected Long compute() {
        long sum = 0;
        boolean canCompute = (end-start)<THRESHOLD;
        if(canCompute){
            System.out.println(start+"\t"+end);
            for(long i =start;i<=end;i++){
                sum+=i;
            }
        }else{
            //分成若干個小任務
            long step = (end-start+1)/10;
            List<CountTask> subTasks = new ArrayList<>();
            for(int i=0;i<Math.ceil((end-start+1)/10.0);i++){
                CountTask countTask = null;
                if((start+step*(i+1)-1)>end){
                    countTask = new CountTask(start+step*i, end);
                }else{
                    countTask = new CountTask(start+step*i, start+step*(i+1)-1);
                }
                subTasks.add(countTask);
                countTask.fork();
            }
            for(CountTask c:subTasks){
                sum+=c.join();
            }
        }
        return sum;
    }

    public static void main(String[] args) throws Exception {
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        CountTask countTask = new CountTask(1, 101);
        ForkJoinTask<Long> forkJoinTask = forkJoinPool.submit(countTask);
        System.out.println(forkJoinTask.get());
    }
}

新建一個可以fork()/join()的任務,由於是計算總和,所以這裏我們需要有返回值的任務,這裏就是繼承RecursiveTask
重寫compute方法,在這個裏面對任務進行分解
針對每一個分解的任務,進行fork操作
針對每一個被分解的任務進行join操作,等待任務執行完畢後的數據的彙總
注意
由於我們這裏邊使用的是遞歸的方式,所以我們需要注意不能將任務劃分的過深,否則會造成下面的問題:
* 系統內的線程越積越多,導致性能的嚴重下降
* 函數的調用層次(遞歸的層次)過多,導致棧溢出

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