Fork/Join——分治編程

Fork/Join

在JDK1.7版本中提供了Fork/Join 並行執行任務框架,它的主要作用是把大任務分割成若干個小任務,再對每個小任務得到的結果進行彙總,此種開發方法也叫分治編程,分治編程可以極大地利用CPU資源,提高任務執行的效率,也是目前與多線程有關的前沿技術。

Fork-Join分治編程與類結構

在這裏插入圖片描述

在JDK中並行執行框架Fork-Join使用了“工作竊取( work- stealing)”算法,它是指某個線程從其他隊列裏竊取任務來執行,那這樣做有什麼優勢或者目的是什麼呢?

比如要完成一個 比較大的任務,完全可以把這個大的任務分割爲若千互不依賴的子任務/小任務,爲了更加方便地管理這些任務,於是把這些子任務分別放到不同的隊列裏,這時就會出現有的線程會先把自己隊列裏的任務快速執行完畢,而其他線程對應的隊列裏還有任務等待處理,完成任務的線程與其等着,不如去幫助其他線程分擔要執行的任務,於是它就去其他線程的隊列裏竊取一個任務來執行,這就是所謂的“工作竊取(work-stealing)” 算法。

在JDK.7中實現中實現分治編程需要使用ForkJoinPool類,此類的主要作用是創建一個任務池,類信息如下:

public class ForkJoinPoll extends AbstractExecutorService{}

該類也是從AbstractExxcutorService類繼承下來的。
在這裏插入圖片描述

類ForkJoinPool所提供的功能是一個任務池,而執行任務卻不是ForkJoinPool,而是ForkJoinTask類。

類FokJoinTask是抽象類,不能實例化,所以需要該類的3個子類CountedCompleter、RecursiveAction和RecursiveTask來實現具體功能。

使用RecursiveAction讓任務跑起來

使用類RecursiveAction執行的任務是具有無返回值的,僅執行一次任務

public class MyTestA {
    public static void main(String[] args) throws InterruptedException {
        ForkJoinPool poll = new ForkJoinPool();
        poll.submit(new MyRecursiveAction());
        Thread.sleep(5000);
    }
}

class MyRecursiveAction extends RecursiveAction {

    @Override
    protected void compute() {
        System.out.println("compute run!");
    }
}

在這裏插入圖片描述

使用RecursiveAction分解任務

前面的示例僅是讓任務運行起來,並打印一個字符串信息,任務並沒有得到fork分解,也就是並沒有體現分治編程的運行效果。在調用ForkJoinTask.java類中的fork() 方法時需要注意一下效率的問題,因爲每一次調用fork 都會分離任務,增加系統運行負擔,所以在ForkJoinTask.java類中提供了public static void invokeAll(ForkJoinTask<?> tl, ForkJoinTask<?>t2)方法來優化執行效率。

public class MyTestA {
    public static void main(String[] args) throws InterruptedException {
        ForkJoinPool poll = new ForkJoinPool();
        poll.submit(new MyRecursiveAction(1, 10));
        Thread.sleep(5000);
    }
}

class MyRecursiveAction extends RecursiveAction {
    private int beginValue;
    private int endValue;

    public MyRecursiveAction(int beginValue, int endValue) {
        this.beginValue = beginValue;
        this.endValue = endValue;
    }

    @Override
    protected void compute() {
        System.out.println(Thread.currentThread().getName() + "------");
        if (endValue - beginValue > 2) {
            int middleNum = (beginValue + endValue) / 2;
            MyRecursiveAction leftAction = new MyRecursiveAction(beginValue, middleNum);
            MyRecursiveAction rightAction = new MyRecursiveAction(middleNum + 1, endValue);
            invokeAll(leftAction, rightAction);
        } else {
            System.out.println("打印組合爲:" + beginValue + "-" + endValue);
        }
    }
}

任務被成功分解。

ForkJoinPool-1-worker-9------
ForkJoinPool-1-worker-9------
ForkJoinPool-1-worker-9------
打印組合爲:1-3
ForkJoinPool-1-worker-2------
ForkJoinPool-1-worker-11------
ForkJoinPool-1-worker-4------
ForkJoinPool-1-worker-2------
打印組合爲:4-5
打印組合爲:6-8
打印組合爲:9-10

使用RecursiveTask取得返回值與join()和get()方法的區別

使用類RecursiveTask執行的任務具有返回值的功能。

public class MyTestB {
    public static void main(String[] args) {
        try {
            MyResursiveTask task1 = new MyResursiveTask();
            System.out.println(task1.hashCode());
            ForkJoinPool pool = new ForkJoinPool();
            ForkJoinTask task2 = pool.submit(task1);
            System.out.println(task2.hashCode() + " " + task2.get());
            Thread.sleep(5000);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class MyResursiveTask extends RecursiveTask<Integer> {

    @Override
    protected Integer compute() {
        System.out.println("compute time=" + System.currentTimeMillis());
        return 100;
    }
}

在這裏插入圖片描述

可以使用join()方法來取得結果值:

public static void main(String[] args) {
        try {
            MyResursiveTask task1 = new MyResursiveTask();
            System.out.println(task1.hashCode());
            ForkJoinPool pool = new ForkJoinPool();
            ForkJoinTask<Integer> task2 = pool.submit(task1);
            System.out.println(task2.hashCode() + " " + task2.join());
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

在這裏插入圖片描述

方法join()與get()雖然都能取得計算後的結果值,但它們之間還是在出現異常時有處理上的區別:

public class MyTestC {
    public static void main(String[] args) {
        try {
            MyRecursiveTaskA taskA = new MyRecursiveTaskA();
            ForkJoinPool pool = new ForkJoinPool();
            ForkJoinTask<Integer> task = pool.submit(taskA);
            System.out.println(task.get());
            for (int i = 0; i < Integer.MAX_VALUE; i++) {
                String newString = new String();
                Math.random();
                Math.random();
                Math.random();
                Math.random();
                Math.random();
                Math.random();
                Math.random();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("main end");
    }
}

class MyRecursiveTaskA extends RecursiveTask<Integer> {

    @Override
    protected Integer compute() {
        System.out.println(Thread.currentThread().getName() + " 執行了compute方法");
        String nullString = null;
        nullString.toString();//NullPoint
        return 100;
    }
}

使用get()方法執行任務時,當子任務出現異常時可以在main主線程中進行捕獲。
在這裏插入圖片描述

在這裏插入圖片描述
在這裏插入圖片描述

使用RecursiveTask執行多個任務並打印返回值

public class MyTestD {
    public static void main(String[] args) throws InterruptedException {
        ForkJoinPool pool = new ForkJoinPool();
        ForkJoinTask<Integer> taskA = pool.submit(new AMyRecursiveTask());
        ForkJoinTask<Integer> taskB = pool.submit(new BMyRecursiveTask());
        System.out.println("準備打印:" + System.currentTimeMillis());
        System.out.println(taskA.join() + " A: " + System.currentTimeMillis());
        System.out.println(taskB.join() + " B: " + System.currentTimeMillis());
        Thread.sleep(5000);
    }
}
class AMyRecursiveTask extends RecursiveTask<Integer> {

    @Override
    protected Integer compute() {
        try {
            System.out.println(Thread.currentThread().getName() + " begin A" + System.currentTimeMillis());
            Thread.sleep(3000);
            System.out.println(Thread.currentThread().getName() + " end B" + System.currentTimeMillis());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return 100;
    }
}
class BMyRecursiveTask extends RecursiveTask<Integer> {

    @Override
    protected Integer compute() {
        try {
            System.out.println(Thread.currentThread().getName() + " begin B" + System.currentTimeMillis());
            Thread.sleep(5000);
            System.out.println(Thread.currentThread().getName() + " end B" + System.currentTimeMillis());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return 100;
    }
}

每個任務成功返回100,並且任務之間運行的方式是異步的,但join()方法卻是同步的。
在這裏插入圖片描述

RecursiveTask實現字符串累加

public class MyTestE {
    public static void main(String[] args) throws InterruptedException {
        ForkJoinPool pool = new ForkJoinPool();
        MyRecursiveTaskForString task = new MyRecursiveTaskForString(1, 20);
        ForkJoinTask<String> task1 = pool.submit(task);
        System.out.println(task1.join());
        Thread.sleep(5000);
    }
}

class MyRecursiveTaskForString extends RecursiveTask<String> {
    private int beginValue;
    private int endValue;

    public MyRecursiveTaskForString(int beginValue, int endValue) {
        this.beginValue = beginValue;
        this.endValue = endValue;
    }

    @Override
    protected String compute() {
        System.out.println(Thread.currentThread().getName() + " ------------");
        if (endValue - beginValue > 2) {
            int middleValue = (endValue + beginValue) / 2;
            MyRecursiveTaskForString leftTask = new MyRecursiveTaskForString(beginValue, middleValue);
            MyRecursiveTaskForString rightTask = new MyRecursiveTaskForString(middleValue + 1, endValue);
            invokeAll(leftTask, rightTask);
            return leftTask.join() + rightTask.join();
        } else {
            String returnString = "";
            for (int i = beginValue; i <= endValue;i++) {
                returnString = returnString +  (i);
            }
            System.out.println("else返回:" + returnString + "       " + beginValue + " " + endValue);
            return returnString;
        }
    }
}

在這裏插入圖片描述

使用Fork-Join實現求和:示例1

public class MyTestF {
    public static void main(String[] args) {
        try {
            MyRecursiveTask task = new MyRecursiveTask(1, 10);
            ForkJoinPool pool = new ForkJoinPool();
            pool.submit(task);
            System.out.println("結果值爲:" + task.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

class MyRecursiveTask extends RecursiveTask<Integer> {
    private int beginPosition;
    private int endPosition;

    public MyRecursiveTask(int beginPosition, int endPosition) {
        this.beginPosition = beginPosition;
        this.endPosition = endPosition;
        System.out.println("# " + (beginPosition + " " + endPosition));
    }

    @Override
    protected Integer compute() {
        System.out.println(Thread.currentThread().getName() + "--------");
        Integer sumValue = 0;
        System.out.println("compute=" + beginPosition + " " + endPosition);
        if ((endPosition - beginPosition) != 0) {
            System.out.println("!=0");
            int middleNum = (endPosition + beginPosition) / 2;
            System.out.println("left 傳入的值:" + (beginPosition + " " + middleNum));
            MyRecursiveTask leftTask = new MyRecursiveTask(beginPosition, middleNum);
            System.out.println("right 傳入的值:" + (middleNum + 1) + " " + endPosition);
            MyRecursiveTask rightTask = new MyRecursiveTask(middleNum + 1, endPosition);

            invokeAll(leftTask, rightTask);

            Integer left = leftTask.join();
            Integer right = rightTask.join();

            return left + right;
        } else {
            return endPosition;
        }
    }
}

在這裏插入圖片描述

使用Fork-Join實現求和:示例2

public class MyTestG {
    public static void main(String[] args) {
        MyRecursiveTaskSum task = new MyRecursiveTaskSum(1, 10);
        ForkJoinPool pool = new ForkJoinPool();
        ForkJoinTask<Integer> submit = pool.submit(task);
        System.out.println("結果值爲:" + submit.join());
    }
}

class MyRecursiveTaskSum extends RecursiveTask<Integer> {
    private int beginPostion;
    private int endPosition;

    public MyRecursiveTaskSum(int beginPostion, int endPosition) {
        this.beginPostion = beginPostion;
        this.endPosition = endPosition;
        System.out.println("# " + (beginPostion + " " + endPosition));
    }

    @Override
    protected Integer compute() {
        Integer sumValue = 0;
        System.out.println("compute=" + beginPostion + " " + endPosition);
        if ((endPosition - beginPostion) > 2) {
            System.out.println("!=0");
            int middleNum = (endPosition + beginPostion) / 2;
            System.out.println("left 傳入的值:" + (beginPostion + " " + middleNum));
            MyRecursiveTaskSum leftTask = new MyRecursiveTaskSum(beginPostion, middleNum);
            System.out.println("right 傳入的值:" + (middleNum + 1) + " " + endPosition);
            MyRecursiveTaskSum rightTask = new MyRecursiveTaskSum(middleNum + 1, endPosition);
            invokeAll(leftTask, rightTask);

            Integer leftValue = leftTask.join();
            Integer rightValue = rightTask.join();
            System.out.println("++++++++++++++++++" + (leftValue + rightValue));
            return leftValue + rightValue;
        } else {
            int count = 0;
            for (int i = beginPostion; i <= endPosition; i++) {
                count += i;
            }
            return count;
        }
    }
}

核心條件爲if((endPosition-beginPosition)>2),也就是可以使用else{}中的for循環以數字範圍的方式進行累加求和。
在這裏插入圖片描述

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