使用Java 7.0的 Fork/Join框架進行併發程式設計

banq發表於2013-05-02
Concurrent programming with Fork/Join framework in Java 7.0

fork/join框架主要是利用多核CPU併發,而之前介紹的Disruptor主要針對單核併發。一般單核情況下,因為運算邏輯的順序性,無法挖掘CPU潛力,CPU利用率比較低,如文中單核圖:

使用Java 7.0的 Fork/Join框架進行併發程式設計

而使用了fork/join框架+多核CPU,CPU利用率就上來了:

使用Java 7.0的 Fork/Join框架進行併發程式設計

其實這篇文章沒有回答一個問題,如何在單核情況下提高CPU利用率,我認為使用Disruptor。

fork/join基於divide-and-conquer演算法,適合將一個大任務劃分為多個小任務,讓多個CPU執行,然後再合併結果,如下圖。如果你不要合併結果,可選方案就很多。

使用Java 7.0的 Fork/Join框架進行併發程式設計

fork/join類似MapReduce演算法,兩者區別是:Fork/Join 只有在必要時如任務非常大的情況下才分割成一個個小任務,而 MapReduce總是在開始執行第一步進行分割。看來,Fork/Join更適合一個JVM內執行緒級別,而MapReduce適合分散式系統

文章案例,需要切割的大任務:產生大量整數。

package mrbool.com;
import java.util.Random;
// This class defines a list which will contain large number of integers.
public class LargeInteger {
    private final int[] list = new int[2000000];
    public largeInteger() {
        Random generator = new Random(19580427);
        for (int i = 0; i < list.length; i++) {
            list[i] = generator.nextInt(500000);
        }
    }
    public int[] getList() {
        return list;
    }
}

<p class="indent">


執行fork/join工作類,將原來需要2百萬的整數任務分成兩個一百萬分別產生:

package mrbool.com;
import java.util.Arrays;
import jsr166y.forkjoin.RecursiveAction;
public class SplitTask extends RecursiveAction {
    private int[] list;
    public long result;
    public SplitTask(int[] array) {
        this.list = array;
    }
    @Override
    protected void compute() {
        if (list.length == 1) {
            result = list[0];
        } else {
            int midpoint = list.length / 2;
            int[] l1 = Arrays.copyOfRange(list, 0, midpoint);
            int[] l2 = Arrays.copyOfRange(list, midpoint, list.length);
            SplitTask s1 = new SplitTask(l1);
            SplitTask s2 = new SplitTask(l2);
            forkJoin(s1, s2);//關鍵
            result = s1.result + s2.result;//合併結果
        }
    }
}

<p class="indent">


交由專門的ForkJoinExecutor 執行,ForkJoinExecutor 和普通Executor 框架,ForkJoinExecutor使用了work-stealing 演算法,輔助執行緒可以從其他還在忙著的執行緒竊取任務。

import jsr166y.forkjoin.ForkJoinExecutor;
import jsr166y.forkjoin.ForkJoinPool;
import javablog.levent.com.SplitTask;
public class TestForkJoin {
    public static void main(String[] args) {
        LargeInteger test = new LargeInteger();
        // Check the number of available processors
        int nThreads = Runtime.getRuntime().availableProcessors();
        System.out.println(nThreads);
        SplitTask mfj = new SplitTask(test.getList());
        ForkJoinExecutor pool = new ForkJoinPool(nThreads);
        pool.invoke(mfj);
        long result = mfj.getResult();
        System.out.println("Done. Result: " + result);     
    }
}

<p class="indent">


總結:Fork/Join框架是專門對付那些邏輯上強順序性強一致性的運算場景,在一些無強一致性場合,我們可以透過簡單的多執行緒(Disruptor)就可以解決,比如你去買A,看到A排隊很長,那麼你可以改變自己的主意,先去買B。而如果你不想改變主意,執意排隊A,那麼Fork/Join框架無疑是個利器,能夠將A隊強行分割成幾個小分隊,你變成“城管”了。

相關文章