Java SE 7の新特性

4460 ワード

java SE 7はjucパッケージを更新し、新たなライト級タスク実行フレームfork/joinとマルチステージthread同期ツールを追加しました。
lightweight task execute frame ebook----fork/jin
    このフレームワークの目的は、ベース層プラットフォーム上のマルチコアcpuとマルチプロセッサを利用して並列処理を行うことであり、問題を解決する際には、分割アルゴリズムまたはmap/reduceアルゴリズムを使用して行う。この名前は使用時の基本的な操作のforkに由来しています。joinは、map/reduceのmapとreduceの操作に類することができます。fork操作は大きな問題をいくつかの小さな問題に分割するもので、この分割過程は通常再帰的に行われ、直接計算できる粒度の適切なサブ問題を得るまでは。分割には、サブ問題の大きさを適切に選択する必要があります。大きすぎる子供の問題は並行して性能を向上させるのに役立ちません。子供の問題は大きな超過費用をもたらします。各サブ問題は計算が完了したら、全体の問題に関する部分解が得られます。ジョンはこれらの部分を解きほぐしたり、結果を集めたりして、最終的に完全な結果を得ます。ジョインを呼び出しても再帰的に行うことができ、fork動作に対応する。
    一般的なthreadpoolの実現に対して、fork/joinの利点は、その中に含まれるタスクの処理方式に反映される。一般的にthreadpoolでthreadが実行しているタスクが何らかの理由で実行できない場合は、このthreadカードの待ち状態です。fork/joinフレームワークの実現において、あるサブ問題が他のサブ問題の完了を待って実行できない場合、このサブ問題を処理するthreadは、他の未実行のサブ問題を探して実行します。この方法は、thread待ち時間を減らし、性能を向上させます。効率的に動作するために、各fork/joinサブ問題において、synchronizedキーワードまたは他の方法を使用して同期することを避けるべきであり、また、ブロッキング式io操作または過剰なアクセス共有変数を使用してはいけない。理想的な情況の下で、すべてのサブ問題の実現の中ですべてcpuの行く先だけを行うべきで、その上すべての問題の内部の対象だけを使って、唯一の同期は子の問題とその父の問題を創建するだけであるべきです。
    fork/joinフレームで実行されるタスクは、ForkJoinTaskクラスによって表されます。Futureインターフェースを実現しました。Futureインターフェースのように使えます。中間には2つの最も重要な方法formとjoinがあり、fork方法は非同期的にタスクの実行を開始し、joinはタスクの完了を待って実行の結果に戻ります。自分のタスクを作成する場合は、ForkJoinTaskから直接引き継がずに、そのサブクラスのRecursiveTaskまたはRecursive Action類から継承したほうがいいです。両者の違いは前者が示すタスクは結果に戻りますが、後者はいけません。
    タスクの実行はForkJoinPool類のオブジェクトで行われます。これはExectorServiceインターフェースを実現し、また一般的なCallable/Runnableインターフェースを使用してタスクを表現することができます。forkjoinpoolで実行されるタスクは、execute、invoke、submitメソッドを通じて直接提出されたタスクと、もう一つは実行中に発生したサブタスクであり、fork方法で実行されます。一般的には、問題全体を表すforkjointask類の対象は第一の形式で提出されますが、実行中に発生したサブタスクは処理する必要がなく、forkjoinpool類の対象はサブタスクの実行を担当します。
    例では、fork/joinを使用して配列の最大値を検索します。各検索タスクは、一定の配列番号区間内で行われます。現在の区間の範囲が大きい場合は、2つのサブ区間に分けて、サブタスクを各サブ区間に作成して検索し、プロセス全体に再帰する。範囲が十分小さいまで、この区間で順番に比較して最大値を検索します。forkでサブタスクを起動し、join方法でサブタスクの実行結果を取得します。サブタスクを実行した後、結果の一部を最終結果に統合するというのが、分割アルゴリズムを用いて行われる典型的な方法です。
    public class MaxVAlue{
        private static final int RANGE_LENGTH=2000;
        private final ForkJoinPool forkJoinPool = new ForkJoinPool();
        private static class MaxValueTask extends RecursiveTask<Long>{
            private final long[] array;
            private final int start;
            private final int end;
            MaxValueTask(long[] array,int start,int end){
                this.array = array;
                this.start = start;
                this.end = end;
            }
            protected Long compute(){
                long max = Long.MIN_VALUE;
                if(end - start <= RANG_LENGTH){
                    for(int i = start;i < end;i++){
                        if(array[i]>max){
                            max = array[i];
                        }
                    }
                }else{
                    int mid = (start+end)/2;
                    MaxValueTask lowTask = new MaxValueTask(array,start,mid);
                    MaxValueTask highTask = new MaxValueTask(array,mid,end);
                    lowTask.fork();
                    highTask.fork();
                    max= Math.max(max,lowTask.join());
                    max = Math.max(max,highTask.join());
                    
                }
                return max;
            }
        }
        //
        public void calculate(long[] array){
            MaxValueTask task = new MaxValueTask(array,0,array.length);
            Long result = forkJoinPool.invoke(task);
            System.out.println(result);
        }
    }
    以上のコード実現方式の効率は、直接的に配列全体を比較する方式より低い。一つのプレゼンテーションだけに使用します。多threadによる超過支出が大きすぎるためです。実際のアプリケーションシーンでは、一つのディレクトリにすべてのテキストファイルの中にあるキーワードを検索すると、各ファイルにサブタスクを作成することができます。この実装の性能は単一threadのルックアップ方法よりも優れています。関連する機能が再帰的および分割アルゴリズムで解決できるなら、fork/joinフレームを使用するのに適している。
マルチステージ同期ツール
    Phaser類は実用的な同期ツールを追加しました。提供された機能と柔軟性は、以前に紹介された逆数ゲートと循環バリアよりも強いです。fork/joinフレームの中性子タスク間で同期する場合は、優先的にPhasher類のオブジェクトを使用します。多threadと連携して実行されるタスクを複数のフェーズphaseに分け、各段階で任意の参加者が参加できるのが特徴です。threadは随時登録して、ある段階の実行に参加できます。フェーズ中にすべてのthreadが成功したら、phaser類の対象は自動的に次の段階に進みます。このphaserオブジェクトに参加者がいなくなるまでループしたら、このphaserオブジェクトの実行は自動的に終了します。オブジェクトが作成された後、その初期段階番号は0であり、構造方法では最初の参加者の個数を指定しても良いし、作成後にregister方法またはbulkregister方法を用いて、1つまたは複数の参加者を動的に追加しても良い。ある参加者がタスクを完了すると、アリaveメソッドを呼び出して声明を出します。一部の参加者は一回の実行が終わったら参加しないでください。アリベand deregister方法を呼び出して声明が完成したら登録をキャンセルします。他の参加者の完成を待つ必要がある場合、arriveand awaitadvanceメソッドを呼び出すことができます。この時、threadはphaser類の対象が次の段階に成功するまで渋滞します。