Java 8新機能その:CompletableFuture


一.CompletableFuture
    1.Futureインタフェース
Future設計の初心:将来のある時点で発生する結果をモデリングします.
非同期計算をモデリングし、演算結果を実行する参照を返します.演算が終了すると、この参照は呼び出し元に返されます.Futureでは、潜在的に時間がかかる操作が呼び出しスレッドを解放し、他の価値のある作業を継続し、時間がかかる操作の完了を待つ必要がなくなります.
Futureの利点:より下位のThreadより使いやすい.
Futureを使用するには、通常、時間のかかる操作をCallableオブジェクトにカプセル化してExecutorServiceにコミットするだけです.
ExecutorService executor = Executors.newCachedThreadPool();
Future future = executor.submit(new Callable() { // ExecutorService    Callable  
    @Override
    public Double call() throws Exception {
        return doSomeLongComputation(); //                  
    }
    });
doSomethingElse(); //         ,         
try {
    //         ,       ,      ,       1      
    Double result = future.get(1, TimeUnit.SECONDS);
} catch (ExecutionException ee) {
    //        
} catch (InterruptedException ie) {
    //             
} catch (TimeoutException te) {
    // Future           
}

    2. 非同期API、コードのブロック回避を実現
工場メソッドsupplyAsyncを使用してCompletableFutureを作成
public Future getPriceAsync2(String product) {
    return CompletableFuture.supplyAsync(() -> calculatePrice(product));
}

supplyAsyncメソッドは、生産者(Supplier)をパラメータとして受け入れ、非同期実行が完了すると、生産者メソッドを呼び出す戻り値を読み出すCompletableFutureオブジェクトを返します.
生産者メソッドはForkJoinPoolプールの実行スレッド(Executor)に渡されますが、supplyAsyncメソッドのリロードバージョンを使用して、2番目のパラメータを渡して異なる実行スレッドを指定して生産者メソッドを実行することもできます.
joinメソッドは非同期操作の終了を待つ
CompletableFutureクラスのjoinメソッドは、Futureインタフェースのgetと同じ意味で、実行が終了するのを待っています.また、Futureインタフェースでは、joinメソッドが検出された異常を放出しない唯一の違いが宣言されています.したがって、try/catch文ブロックを使用する必要はありません.
public List findPrices(String product) {
//  CompletableFuture              
List> priceFutures = shops.stream()
.map(shop -> CompletableFuture.supplyAsync(() -> shop.getName() + " price is " + shop.getPriceAsync(product)))
.collect(Collectors.toList());
//          
return priceFutures.stream().
map(CompletableFuture::join)
.collect(Collectors.toList());
}

カスタムアクチュエータの使用
有線プールのエフェクタを作成します.
スレッド数の選択:N(threads)=N(CPU)*U(CPU)*(1+W/C)
--N(CPU):プロセッサのコア数は、Runtime.getRuntime().义齿
--U(CPU):所望のCPU利用率(この値は0と1の間であるべきである);
--W/C:計算時間に対する待ち時間の比率.
//       ,          100              (  100       )
private final Executor executor = Executors.newFixedThreadPool(Math.min(shops.size(), 100), new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setDaemon(true); //      --             
return t;
}
});

パラレル計算には、パラレルフローとCompletableFuturesの2つの方法があります.
--密な操作を計算し、I/Oがない場合は、Streamインタフェースを推奨します.実装が簡単で、効率も最も高い可能性があります(すべてのスレッドが計算が密集している場合、プロセッサコア数よりも多くのスレッドを作成する必要はありません).
--パラレルワークセルがI/O待ちの操作(ネットワーク接続待ちを含む)にも関与している場合、CompletableFutureを使用する方が柔軟性が優れています.この場合、処理フローのパイプラインでI/O待ちが発生した場合、フローの遅延特性はいつ待機がトリガーされたのか判断しにくくなります.
    3. 複数の非同期タスクのパイプライン操作
thenApply:文字列からQuoteを変換する方法をパラメータとして彼に渡す
thenCompose:この方法では、2つの非同期操作に対して流水線を行うことができ、最初の操作が完了すると、その結果をパラメータとして2番目の操作に渡すことができます.
最初のCompletableFutureオブジェクトに対してthenComposeを呼び出し、関数を渡します.最初のCompletableFutureが実行されると、彼の結果はその関数のパラメータとなり、この関数の戻り値は最初のCompletableFutureの戻りを入力として計算された2番目のCompletableFutureオブジェクトとなる.
thenComposeを使用すると、多くのスレッド切り替えのオーバーヘッドが削減されます.
thenCombine:2つのCompletableFutureオブジェクトの結果を統合します.このメソッドはBiFunctionという名前の2番目のパラメータを受信し、このパラメータは2つのCompletableFutureオブジェクトが計算を完了した後、どのようにマージするかを定義します.
thenAccept:メソッドはCompletableFuture実行後の戻り値をパラメータとして受信する.まだ戻っていない結果を待つ必要はありません.