同時プログラミング実戦10-マルチスレッドでのタスク分解メカニズムForkJoinPool詳細

4647 ワード

Fork/JoinモードはMapReduceに似ており、分割された理念にも相当し、あるいは二分検索、二重集計アルゴリズムのようなものである.1つの大量の計算を多数の小さな計算に分解することにより,分割して処理し,その後統合するとともに,これらの分割した各小さな計算は並列に行われ,CPUの利用率が大幅に増大する.
Fork/Joinモードには独自の適用範囲があります.1つのアプリケーションが複数のサブタスクに分解され、複数のサブタスクを組み合わせた結果、最終的な答えが得られる場合、このアプリケーションはFork/Joinモードで解決するのに適している.図1は、図上部に位置するTaskが、その下に位置するTaskの実行に依存し、すべてのサブタスクが完了した後にのみ、呼び出し元がTask 0の戻り結果を得ることができるFork/Joinモードの概略図を示す.Fork/Joinモードは多くの種類の並列問題を解決できると言える.Doug Leaが提供するFork/Joinフレームワークを使用することで、ソフトウェア開発者はタスクの区分と中間結果の組み合わせに注目するだけで、パラレルプラットフォームの優れた性能を十分に利用することができます.負荷バランス、同期など、並列に関連する多くの処理が困難な問題は、フレームワークによって統一的に解決することができる.これにより、パラレルプログラミングの困難でエラーが発生しやすいという欠点を回避し、パラレルのメリットを簡単に得ることができます.
マルチスレッド同時プログラミングでは、大きなタスクを小さなタスクに分解して同時実行するシーンに遭遇することがあります.Java 8に追加されたForkJoinPoolは、この問題をよくサポートしています.ForkJoinPoolは、タスクの分解をサポートするスレッドプールで、タスクに「大きすぎる」とコミットすると、事前に定義されたルールに従って大きなタスクを小さなタスクに分解し、マルチスレッドが同時に実行します.一般的には、分解可能なタスクインタフェースForkJoinTaskと組み合わせて使用されます.ForkJoinTaskには、RecursiveActionとRecursiveTaskの2つの抽象クラスがあります.前者は戻り値がなく、後者は戻り値があります.
  • アプリケーションシーン:簡単に言えば、あなたの問題がサブ問題に簡単に分解できれば、ForkJoinPoolに適しています.CPU密集型のシーンに適しています.
  • 例:1+2+...+100を計算する.並列計算
  • package com.sound.daytc1;
    
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.ForkJoinPool;
    import java.util.concurrent.Future;
    import java.util.concurrent.RecursiveTask;
    
    /**
     * @author: ZouTai
     * @date: 2018/4/10
     * @description: ForkJoin        -   
     */
    public class RecursiveTaskDemo extends RecursiveTask<Integer> {
        private int first;
        private int last;
    
        public RecursiveTaskDemo(int first, int last) {
            this.first = first;
            this.last = last;
        }
    
        @Override
        protected Integer compute() {
            System.out.println(Thread.currentThread().getName() + " ... ");
            /**
             *             
             *   ForkJoin
             */
            int sum = 0;
            //     
            if (last - first <= 2) {
                //   
                for (int i = first; i <= last; i++) {
                    sum += i;
                }
            } else {
                /**
                 *          
                 */
                RecursiveTaskDemo demo01 = new RecursiveTaskDemo(first, (last + first) / 2);
                RecursiveTaskDemo demo02 = new RecursiveTaskDemo((last + first) / 2 + 1, last);
    
                //   
                demo01.fork();
                demo02.fork();
    
                Integer a = demo01.join();
                Integer b = demo02.join();
    
                sum = a + b;
            }
            return sum;
        }
    
        public static void main(String[] args) {
            ForkJoinPool forkJoinPool = new ForkJoinPool(3);
            Future future = forkJoinPool.submit(new RecursiveTaskDemo(1, 100));
            System.out.println("      ...");
            try {
                System.out.println("     :" + future.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
    }
    

    IBM-JDK 7でのFork/Joinモード