Java線程學習筆記之並行計算

Fork/Join

在JDK7之後,Java加入了並行計算的框架Fork/Join,來解決系統中大數據計算的性能問題。在這裏要強調的是並行並不是併發,並行是指系統內有多個任務同時執行,併發是指系統內有多個任務同時存在。不同的任務按時間分片的方式切換執行,由於切換的時間很短,給人的感覺好像是在同時執行。
Fork/Join採用的是分治法,Fork是講一個大任務拆分成若干個子任務,子任務分別去計算,而Join是獲取子任務的計算結果,然後合併。就像分佈式裏面的MapReduce一樣。Java的Fork/Join是一個遞歸的過程,子任務被分配到不同的核上執行,效率最高。

ForkJoinPool

Fork/Join框架的核心類是ForkJoinPool,ForkJoinPool是ExecutorService的一個實例,它能夠接收一個ForkJoinTask,並得到計算結果。ForkJoinTask有兩個子類, RecursiveAction(無返回結果), RecursiveTask(有返回結果)。定義任務時,只要繼承這兩個類就可。
類圖
下面是一個實例,計算一個超大數組所有元素的和。

public class ForkJoinPoolTest extends RecursiveTask<Integer>{

    private static final long serialVersionUID = 1L;
    private static final int THRESHOLD = 100;//閾值
    private long[] array;
    private int low;
    private int high;

    public ForkJoinPoolTest(long[] array, int low, int high) {
        this.array = array;
        this.low = low;
        this.high = high;
    }

    @Override
    protected Integer compute() {
        int sum = 0;
        if(high - low <= THRESHOLD){
            //小於閾值這直接計算
            for(int i=low;i<high;i++){
                sum += array[i];
            }
        } else{
            //將一個大任務分成兩個小任務
            int mid = (low + high) >>> 1;
            ForkJoinPoolTest left = new ForkJoinPoolTest(array, low, mid);
            ForkJoinPoolTest right = new ForkJoinPoolTest(array, mid+1, high);

            //分別計算
            left.fork();
            right.fork();

            sum = left.join() + right.join();
        }
        return sum;
    }

    public static void main(String[] args) throws InterruptedException, ExecutionException{
        long[] array = geeArray(50000);
        System.out.println(Arrays.toString(array));

        //創建任務
        ForkJoinPoolTest task = new ForkJoinPoolTest(array, 0, array.length - 1);
        long begin = System.currentTimeMillis();
        //創建線程池
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        //提交任務到線程池
        forkJoinPool.submit(task);
        //獲取結果
        Integer result = task.get();
        long end = System.currentTimeMillis();
        System.out.println(String.format("結果 %s 耗時 %s ms", result, end - begin));
    }

    private static long[] geeArray(int size){
        long[] array = new long[size];
        for(int i=0;i<array.length;i++)
            array[i] = new Random().nextLong();
        return array;
    }
}

通過調整閾值(THRESHOLD),可以發現耗時是不一樣的。

本文參考自:http://blog.csdn.net/ghsau/article/details/46287769

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