JAva 8同時プログラミング実戦-part 1


技術バージョン
jdk: 1.8
前言
ネット上の投稿は千万で、私たちも一緒に冷たいご飯を炒めます.CompletableFutureは非同期プログラミングを構築するための基礎であり、同時にFutureとCompletionStageから受け継がれています.CompletionStageというインタフェースはpromiseで、この計算が最終的に完成することを示しています.
Futureの不足
1.操作完了e.g.を手動で設定することはできません.個別のスレッドを開いてrpcでインタフェースを調整していくつかのデータを返します.下流のインタフェースが切れた場合、前回キャッシュしたデータを返すことで手動で完了したいと思っています.この場合、Futureはこのようなapiサポートを持っていません.2.ブロック式の操作Futureの戻り結果e.gは、結果が利用可能になった場合、Futureは自発的に通知しません.get()メソッドを呼び出して結果を返すのを待つしかありません.Futureにコールバックを設定することはできません.構造が使用可能なときに自動的にコールバックを呼び出します.3.複数のFutureはチェーン呼び出しe.gを行うことができず、例えば2つの時間のかかる計算(A,B)があり、彼らは依存関係があり、A計算が完了すると、Bに伝達して計算を継続する必要がある.Futureでは非同期のフロー計算呼び出しはサポートされていません.4.複数のFuture e.gの組み合わせはサポートされていません.例えば、5つの同時サポートされているFutureがあります.私たちは彼らがすべて実行した後、追加の操作をしたいと思っています.Futureはサポートされていません.5.異常処理Future apiが異常処理を提供していないメカニズム
ケースの開始
最も単純な非同期
@Test
public void test1() throws ExecutionException, InterruptedException {
    CompletableFuture completableFuture = new CompletableFuture<>(); //step1
    new Thread(new CompleteFutureTask(completableFuture)).start(); //step2
    String result = completableFuture.get(); //step3
    System.out.println(Thread.currentThread().getName() + " get result:" + result);
}

class CompleteFutureTask implements Runnable {
    CompletableFuture completableFuture;

    public CompleteFutureTask(CompletableFuture completableFuture) {
        this.completableFuture = completableFuture;
    }

    @Override
    public void run() {
        MixAll.simulateComputeCost();
        System.out.println(Thread.currentThread().getName() + " will complete future:" + completableFuture);
        completableFuture.complete("hello completableFuture"); //step4
        //completableFuture.complete("hello completableFuture"); //step5
    }
}

Step 1最も簡単な方法でCompletableFutureを作成します.step 2がなければ、step 3にあるget()メソッドは計算が完了していないため、ブロックされ続けます.step 4の複数回の呼び出しは無効です.すなわち、step 5は無効です.
runAsync()で非同期計算を行い、メソッドを変更してCompletableFutureを返します.
サンプルコード
@Test
public void runAsyncTest() throws ExecutionException, InterruptedException {
    CompletableFuture cf = CompletableFuture.runAsync(() -> {
        MixAll.simulateComputeCost();
        System.out.println(Thread.currentThread().getName() + " runAsyncTest...................");
    });
    cf.get(); //step1
    //MixAll.simulateComputeCost(8); //step2
}

これはjunitの中で行われるので、実行が完了するのを待つ必要があります.待つ方法は2種類あります:step 1とstep 2. get()メソッドを呼び出す必要があると勘違いしないでください.  この方法には2つのリロード方法がある
public static CompletableFuture runAsync(Runnable runnable)  
public static CompletableFuture runAsync(Runnable runnable,Executor executor)  

メソッド1はForkJoinPoolのスレッドプールを使用し,メソッド2はカスタムスレッドプールに転送できる.
supplyAsync()は非同期計算を行い、戻り結果がある
サンプルコード
@Test
public void supplyAsyncTest() throws ExecutionException, InterruptedException {
    CompletableFuture cf = CompletableFuture.supplyAsync(() -> {
        MixAll.simulateComputeCost();
        MixAll.printWithThread(" will return something");
        return "hello supplyAsync";
    });

    String data = cf.get();
    MixAll.printWithThread(" get data:" + data);
}

依然として2つのリロード方法があり、詳しくはapi、スレッドプールの説明を参照してください.
非同期CompletableFutureの構築
上のget()はブロックされています.彼は実行が完了するまでブロックします.これが最初に私たちが言ったFutureの不足です.これは私たちが望んでいるものではありません.私たちが必要なのはコールバックを設定して、実行が完了したら自動的に呼び出すことができます.次に、これらの方法を見てみましょう.1.thenApply()方法は、参照としてFunctionを受け入れ、同時に参照付きCompletableFutureに戻ります. サンプルコード
@Test
public void thenApplyTest() throws ExecutionException, InterruptedException {
    CompletableFuture cf = CompletableFuture
            .supplyAsync(() -> {
                MixAll.simulateComputeCost();
                MixAll.printWithThread(" will return something");
                return "hello supplyAsync";
            })
            .thenApply(info -> {
                MixAll.simulateComputeCost();
                MixAll.printWithThread(" will return something");
                return "thenApply|" + info;
            });


    String data = cf.get();
    MixAll.printWithThread(" get>>" + data);
}

supplyAsyncの実行が完了すると、thenApply()メソッドを自動的に呼び出してthenApply内の論理を実行するスレッドと、supplyAsync論理を実行するスレッドは同じである.
2.thenAccept()は、CompletableFutureを返します.パラメータはConsumerサンプルコードです.まず非同期でデータを取得し、完了したら消費します.
@Test
public void thenAcceptTest() {
    CompletableFuture.supplyAsync(() -> UserService.Instance.getUser(12))
            .thenAccept(user -> MixAll.printWithThread(" thenAccept get userName:" + user.getName()));

    //      
    MixAll.simulateComputeCost(8);
}

3.thenRun()は、CompletableFutureを返します.パラメータはRunnableです.前のステップの戻り結果(戻り結果がある場合)のサンプルコードにアクセスできません.
@Test
public void thenRunTest() {
    long start = System.currentTimeMillis();
    CompletableFuture
            .runAsync(() -> UserService.Instance.getUser(12))
            .thenRun(() -> MixAll.printWithThread(" thenRun cost:" + (System.currentTimeMillis() - start) + "ms"));
    //      
    MixAll.simulateComputeCost(8);
}

この3つの方法はいずれも他の2つの接尾辞がAsyncのリロード方法であり,thenApplyを例にとると
thenApplyAsync(Function super T,? extends U> fn)  //method1
thenApplyAsync(Function super T,? extends U> fn, Executor executor)   //method2  

Method 1はメソッド内部でForkJoinPool.commonPool()を使用していますが、テスト中にthenApplyAsyncを実行するロジックとsupplyAsyncを実行するスレッドが同じであることがわかり、またオンラインプールで同じものを手に入れたと推測されますが、あいにくですね...method 2は伝達されたexecutorを使用して実行しています
未完待续....
微信の公衆番号に注目してください.  ぎせいプログラマ