CompletableFuture:あなたのコードは、閉塞に苦しんで作ります

メインスレッドは、可能な限り迅速に対応できるように、非同期を考えるのは簡単なタスクの数を処理するのに非同期であるとき、アプリケーションのパフォーマンスを向上させます。

EDITORIAL

この記事を読んだことによって、あなたは学ぶことができます:

  • CompletableFutureの使用
  • CompletableFure非同期および同期性能試験
  • 将来なぜCompletableFutureはまだJDK1.8で導入することが必要になりました
  • CompletableFutureアプリケーションシナリオ
  • CompletableFutureの使用の最適化

シーン記述

出会うすべてのお店や商品価格、getPriceと呼ばれる同期方法を提供ショップクラスを同期するための店舗や商品のAPIのクエリ価格に戻ります

  • ショップカテゴリ:Shop.java
public class Shop {
    private Random random = new Random();
    /**
     * 根据产品名查找价格
     * */
    public double getPrice(String product) {
        return calculatePrice(product);
    }

    /**
     * 计算价格
     *
     * @param product
     * @return
     * */
    private double calculatePrice(String product) {
        delay();
        //random.nextDouble()随机返回折扣
        return random.nextDouble() * product.charAt(0) + product.charAt(1);
    }

    /**
     * 通过睡眠模拟其他耗时操作
     * */
    private void delay() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
复制代码

睡眠アナログ方式による問合せ商品価格の同期方法、および他の操作。あなたは、サードパーティのAPIを呼び出す必要があるときに、このシナリオがシミュレートしていますが、パフォーマンスを設計し、アプリケーションのスループットを向上させるためにどのようにサードパーティのAPIを呼び出すためにコードを変更できない場合、サードパーティが提供するAPIは、これを使用することができ、同期されたときにCompletableFutureクラス

CompletableFutureの使用

Completable今後の実装クラスのインターフェイスは、JDK1.8で導入されました

  • CompletableFutureの作成:

    • 新しいメソッドを使用して

      CompletableFuture<Double> futurePrice = new CompletableFuture<>();
      复制代码
    • CompletableFuture#completedFutureは、静的メソッドを使用して作成しました

      public static <U> CompletableFuture<U> completedFuture(U value) {
          return new CompletableFuture<U>((value == null) ? NIL : value);
      }
      复制代码

      タスクの実行結果パラメータが終了し、一般的な方法は、ほとんどの実用的なアプリケーションで使用されていません

    • supplyAsync使用CompletableFuture#supplyAsyncの静的メソッドを作成するには、2つのオーバーロードメソッドがあります。

      //方法一
      public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
          return asyncSupplyStage(asyncPool, supplier);
      }
      //方法二
      public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,
                                                         Executor executor) {
          return asyncSupplyStage(screenExecutor(executor), supplier);
      }
      复制代码
    • CompletableFuture#runAsyncを使用してrunAsyncオーバーロードされた静的メソッドを作成するには、2つの方法があります。

      //方法一
      public static CompletableFuture<Void> runAsync(Runnable runnable) {
          return asyncRunStage(asyncPool, runnable);
      }
      //方法二
      public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor) {
          return asyncRunStage(screenExecutor(executor), runnable);
      }
      复制代码

    説明:

    • エグゼキュータをカスタマイズするために渡される2つのオーバーロード=>との違いは、前者がデフォルト、使用ForkJoinPoolです
    • 方法supplyAsyncとrunAsyncの差は=>前者には戻り値はありません戻り値を持ちます
    • サプライヤーは、関数インタフェースで、このメソッドは、このインタフェースは、runメソッド内のメソッドを見つけるのソースコードを追跡するインターフェイスを呼び出して実装することを、着信クラスが必要です。したがってCompletableFutureは、単にプロセスで取得するために定義されたタスクのgetメソッドサプライヤをオーバーライドし、この方法を使用してオブジェクトを作成します。関数インタフェースは、ラムダ式を使用して、作成することができますので、また、新しいオブジェクトがCompletableFutureコード比べて簡潔に多くのことを
  • 買収の結果: CompltableFutureクラスの結果を得るためには、4つの方法を提供します

    //方式一
    public T get()
    //方式二
    public T get(long timeout, TimeUnit unit)
    //方式三
    public T getNow(T valueIfAbsent)
    //方式四
    public T join()
    复制代码

    説明:

    • 取得()と(長いタイムアウト、TimeUnitで単位)を取得=>将来の結果が指定された時間内に取得されない場合、タイムアウト処理を提供し、提供された例外のタイムアウトをスローします
    • getNow =>次に、計算の異常な結果は、完了した又は完了設定値がvalueIfAbsent返される場合、計算の戻り結果は、計算されていないブロックされていない結果を得ます
    • =>メソッドでスローしません参加

    例:

    public class AcquireResultTest {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            //getNow方法测试
            CompletableFuture<String> cp1 = CompletableFuture.supplyAsync(() -> {
                try {
                    Thread.sleep(60 * 1000 * 60 );
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
                return "hello world";
            });
    
            System.out.println(cp1.getNow("hello h2t"));
    
            //join方法测试
            CompletableFuture<Integer> cp2 = CompletableFuture.supplyAsync((()-> 1 / 0));
            System.out.println(cp2.join());
    
            //get方法测试
            CompletableFuture<Integer> cp3 = CompletableFuture.supplyAsync((()-> 1 / 0));
            System.out.println(cp3.get());
        }
    }
    复制代码

    説明:

    • あなたは1分間のスリープにしたいので、最初の実行結果は、すぐに結果を得ることができない、ハローH2Tです
    • 例外をスローしないであろう方法で結果を得るために参加するが、結果は例外がスローされます、例外がスローされますCompletionException
    • 例外がスローされます方法で結果を得るために取得するには、スローされた例外の結果はExecutionExceptionです
  • 例外処理: CompletableFutureの静的メソッドを作成するには、例外処理を表示せずにオブジェクトのキャプチャ異常にcompleteExceptionallyセットと呼ばれる新しいメソッドのニーズを使用して作成されたオブジェクト、例えば:

    CompletableFuture completableFuture = new CompletableFuture();
    new Thread(() -> {
         try {
             //doSomething,调用complete方法将其他方法的执行结果记录在completableFuture对象中
             completableFuture.complete(null);
         } catch (Exception e) {
             //异常处理
             completableFuture.completeExceptionally(e);
          }
     }).start();
    复制代码

クエリにすべてのお店、いくつかの商品価格の同期方法非同期メソッドを選択

ショップリストです:

private static List<Shop> shopList = Arrays.asList(
        new Shop("BestPrice"),
        new Shop("LetsSaveBig"),
        new Shop("MyFavoriteShop"),
        new Shop("BuyItAll")
);
复制代码

同期方式:

private static List<String> findPriceSync(String product) {
    return shopList.stream()
            .map(shop -> String.format("%s price is %.2f",
                    shop.getName(), shop.getPrice(product)))  //格式转换
            .collect(Collectors.toList());
}
复制代码

非同期メソッド:

private static List<String> findPriceAsync(String product) {
    List<CompletableFuture<String>> completableFutureList = shopList.stream()
            //转异步执行
            .map(shop -> CompletableFuture.supplyAsync(
                    () -> String.format("%s price is %.2f",
                            shop.getName(), shop.getPrice(product))))  //格式转换
            .collect(Collectors.toList());

    return completableFutureList.stream()
            .map(CompletableFuture::join)  //获取结果不会抛出异常
            .collect(Collectors.toList());
}
复制代码

パフォーマンステストの結果:

Find Price Sync Done in 4141
Find Price Async Done in 1033
复制代码

非同期実行効率4倍に増加

なぜまだ必要CompletableFuture

JDK1.8では前に、そのタスクはメソッドを提出することができますことをgetメソッドを呼び出すことにより、結果非同期実行を取得し、今後のオブジェクトを返す、非同期で実行するスレッドプールを呼び出します。

private static List<String> findPriceFutureAsync(String product) {
    ExecutorService es = Executors.newCachedThreadPool();
    List<Future<String>> futureList = shopList.stream().map(shop -> es.submit(() -> String.format("%s price is %.2f",
            shop.getName(), shop.getPrice(product)))).collect(Collectors.toList());

    return futureList.stream()
            .map(f -> {
                String result = null;
                try {
                    result = f.get();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }

                return result;
            }).collect(Collectors.toList());
}
复制代码

まだCompletableFutureを導入する必要がある理由シェンゆう盛亮、どちら?
将来を使って簡単なビジネスシナリオはありませんでしたが、組み合わせた複数の非同期タスクの結果を計算するために、計算後の非同期タスクは、ポイントAPI今後は、袋の上に提供することを、値の前、及びその非同期タスクに使用することが必要CompletableFutureせるために、この時間をに対処するためのエレガントな十分な恥ずかしがり屋、宣言これらに対処する方法は、優雅さを要求

他のAPIの紹介

whenComplete計算プロセス:

上記処理の結果は、新たな値が返されない
3つのメソッドを提供します。

//方法一
public CompletableFuture<T> whenComplete(BiConsumer<? super T,? super Throwable> action)
//方法二
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action)
//方法三
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action, Executor executor)
复制代码

説明:

  • BiFunctionは、FN =パラメータ>定義結果を処理<?スーパーT、?スーパーU、?Vが延びます>
  • エグゼキュータのパラメータ=>カスタム・スレッド・プール
  • 新しいスレッドで非同期メソッドの最後に操作の組み合わせを実行します

例:

public class WhenCompleteTest {
    public static void main(String[] args) {
        CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> "hello");
        CompletableFuture<String> cf2 = cf1.whenComplete((v, e) ->
                System.out.println(String.format("value:%s, exception:%s", v, e)));
        System.out.println(cf2.join());
    }
}
复制代码

thenApply変換:

計算結果は、処理後の正面CompletableFuture thenApply、thenApplyリターン結果に送信されます。それはthenApply方法によって達成考えることができるCompletableFuture<T>CompletableFuture<U>変換。パラメータCompletableFuture thenApply方法はthenApply法の処理結果を返すように方言点は、計算結果である
3つのメソッドを提供します。

//方法一
public <U> CompletableFuture<U> thenApply(
    Function<? super T,? extends U> fn) {
    return uniApplyStage(null, fn);
}

//方法二
public <U> CompletableFuture<U> thenApplyAsync(
    Function<? super T,? extends U> fn) {
    return uniApplyStage(asyncPool, fn);
}

//方法三
public <U> CompletableFuture<U> thenApplyAsync(
    Function<? super T,? extends U> fn, Executor executor) {
    return uniApplyStage(screenExecutor(executor), fn);
}
复制代码

説明:

  • 関数fn =パラメータ>計算結果CompletableFutureに変換操作前<?スーパーTは、?U延び>
  • エグゼキュータのパラメータ=>カスタム・スレッド・プール
  • 結合動作は例で非同期メソッドの最後に新しいスレッドを実行します。
public class ThenApplyTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<Integer> result = CompletableFuture.supplyAsync(ThenApplyTest::randomInteger).thenApply((i) -> i * 8);
        System.out.println(result.get());
    }

    public static Integer randomInteger() {
        return 10;
    }
}
复制代码

CompletableFuture前の8倍の拡大ここで計算した結果

プロセスのthenAccept結果:

thenApplyもthenAcceptとthenApply差が戻り値は、結果の処理として分類することができる
3つのメソッドを提供しません。

//方法一
public CompletableFuture<Void> thenAccept(Consumer<? super T> action) {
    return uniAcceptStage(null, action);
}

//方法二
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action) {
    return uniAcceptStage(asyncPool, action);
}

//方法三
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action,
                                               Executor executor) {
    return uniAcceptStage(screenExecutor(executor), action);
}
复制代码

説明:

  • 前回の計算結果CompletableFutureの消費者<?スーパーT>アクションパラメータ=>運転
  • エグゼキュータのパラメータ=>カスタム・スレッド・プール
  • 動作の同様の典型的な組み合わせは、非同期メソッドの最後に、新しいスレッドで実行されます。
public class ThenAcceptTest {
    public static void main(String[] args) {
        CompletableFuture.supplyAsync(ThenAcceptTest::getList).thenAccept(strList -> strList.stream()
                .forEach(m -> System.out.println(m)));
    }

    public static List<String> getList() {
        return Arrays.asList("a", "b", "c");
    }
}
复制代码

計算結果はCompletableFuture前にプリントアウトされています

thenCompose非同期パイプラインの結果:

thenCompose 2つの方法があってもよい非同期動作のパイプライン化は、
3つのメソッドを提供します。

//方法一
public <U> CompletableFuture<U> thenCompose(
    Function<? super T, ? extends CompletionStage<U>> fn) {
    return uniComposeStage(null, fn);
}

//方法二
public <U> CompletableFuture<U> thenComposeAsync(
    Function<? super T, ? extends CompletionStage<U>> fn) {
    return uniComposeStage(asyncPool, fn);
}

//方法三
public <U> CompletableFuture<U> thenComposeAsync(
    Function<? super T, ? extends CompletionStage<U>> fn,
    Executor executor) {
    return uniComposeStage(screenExecutor(executor), fn);
}
复制代码

説明:

  • Function<? super T, ? extends CompletionStage<U>> fn実行パラメータ=>現在の演算結果CompletableFuture
  • エグゼキュータのパラメータ=>カスタム・スレッド・プール
  • 同様の操作は、非同期メソッドの最後に新しいスレッドの組み合わせで実行される
    例:
public class ThenComposeTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<Integer> result = CompletableFuture.supplyAsync(ThenComposeTest::getInteger)
                .thenCompose(i -> CompletableFuture.supplyAsync(() -> i * 10));
        System.out.println(result.get());
    }

    private static int getInteger() {
        return 666;
    }

    private static int expandValue(int num) {
        return num * 10;
    }
}
复制代码

実行フロー:

結果のthenCombineの組み合わせ:

thenCombine CompletableFutureを組み合わせ二つの独立した方法、第2 Completable第Completableの結果に依存しない
3つのメソッドを提供します。

//方法一
public <U,V> CompletableFuture<V> thenCombine( 
    CompletionStage<? extends U> other,
    BiFunction<? super T,? super U,? extends V> fn) {
    return biApplyStage(null, other, fn);
}
  //方法二
  public <U,V> CompletableFuture<V> thenCombineAsync(
      CompletionStage<? extends U> other,
      BiFunction<? super T,? super U,? extends V> fn) {
      return biApplyStage(asyncPool, other, fn);
  }

  //方法三
  public <U,V> CompletableFuture<V> thenCombineAsync(
      CompletionStage<? extends U> other,
      BiFunction<? super T,? super U,? extends V> fn, Executor executor) {
      return biApplyStage(screenExecutor(executor), other, fn);
  }
复制代码

説明:

  • CompletableFutureのCompletionStage <?拡張U>その他のパラメータ=>新しい計算
  • BiFunctionは<?スーパーT、?スーパーU FN V、?延び> =パラメータ>定義二つCompletableFutureオブジェクトの計算が完了した後の結果を結合する方法、このパラメータは機能インタフェースでは、ラムダ式を使用することが可能です
  • エグゼキュータのパラメータ=>カスタム・スレッド・プール
  • 同じ組み合わせは、新しいスレッドで非同期メソッドの最後に操作を実行します

例:

public class ThenCombineTest {
    private static Random random = new Random();
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<Integer> result = CompletableFuture.supplyAsync(ThenCombineTest::randomInteger).thenCombine(
                CompletableFuture.supplyAsync(ThenCombineTest::randomInteger), (i, j) -> i * j
        );

        System.out.println(result.get());
    }

    public static Integer randomInteger() {
        return random.nextInt(100);
    }
}
复制代码

2つのスレッドがフローチャートに戻りで行われる乗算の計算値を作ります。

複数のCompletableFutureを組み合わせALLOF&anyOf。

方法はじめに:

//allOf
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs) {
    return andTree(cfs, 0, cfs.length - 1);
}
//anyOf
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs) {
    return orTree(cfs, 0, cfs.length - 1);
}
复制代码

説明:

  • ALLOF =>すべてのCompletableFutureは、計算を実行した後に行います。
  • anyOf =>任意の計算をCompletableFuture後に実行される実行します

例:

  • 試験方法ALLOF
    public class AllOfTest {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            CompletableFuture<Void> future1 = CompletableFuture.supplyAsync(() -> {
                System.out.println("hello");
                return null;
            });
            CompletableFuture<Void> future2 = CompletableFuture.supplyAsync(() -> {
                System.out.println("world"); return null;
            });
            CompletableFuture<Void> result = CompletableFuture.allOf(future1, future2);
            System.out.println(result.get());
        }
    }
    复制代码
    ALLOF方法は、戻り値は、戻り値と以前のすべてのタスクのための必要はありませんが、フォローアップタスクを実行するには、完成したシナリオではありません
  • anyOf試験方法
    public class AnyOfTest {
        private static Random random = new Random();
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
                randomSleep();
                System.out.println("hello");
                return "hello";});
            CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
                randomSleep();
                System.out.println("world");
                return "world";
            });
            CompletableFuture<Object> result = CompletableFuture.anyOf(future1, future2);
            System.out.println(result.get());
       }
    
        private static void randomSleep() {
            try {
                Thread.sleep(random.nextInt(10));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    复制代码
    2つのスレッドが結果をプリントアウトしますが、getメソッドは、唯一のタスクを完了するために最初から結果を返します。このアプローチは良いです限り、戻り値があるとして、他のタスクのアプリケーションシナリオを実行し続けることができます

注意点

多くの方法が[と]非同期非同期サフィックスを達成するために利用可能であるが、非同期の手段は、コンテキストの切り替え、同期が必ずしも良いパフォーマンスよりも場合がありますので、これらの必要性は、非同期メソッドを使用するように注意します。あなたは非同期メソッドを使用する必要がある場合は最初のテストを行い、テストデータと話します!

CompletableFutureアプリケーションシナリオ

CompletableFuture、実行する別のスレッドに引き渡さIOの一部を選択することができIO集約型のタスクがあります。- Logback、実装原理Log4j2非同期ロギングがIO動作を実行するための新しいスレッドが再生され、この部分はCompletableFuture.runAsync(> {ioOperation()であることができる ;()}) 関連Logback非同期ロギングを起動する方法原理はこの記事を参照することができますLogback非同期ログCPUを集中的に使用することは推奨されていない場合はパラレル・ストリームを使用することをお勧めします

スペースの最適化

supplyAsync基本となる実装タスク:

public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
    return asyncSupplyStage(asyncPool, supplier);
}
static <U> CompletableFuture<U> asyncSupplyStage(Executor e, Supplier<U> f) {
    if (f == null) throw new NullPointerException();
    CompletableFuture<U> d = new CompletableFuture<U>();
    e.execute(new AsyncSupply<U>(d, f));
    return d;
}
复制代码

低レベルの呼び出しは、スレッドプールは、タスクを実行すること、およびCompletableFutureは、デフォルトのスレッド・プールはForkJoinPoolです

private static final Executor asyncPool = useCommonPool ?
        ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();
复制代码

ForkJoinPoolスレッドプールのサイズは、CPUコアの数に依存します。書き込む前にエグゼキュータは、スレッドプールを作成無効にするにはなぜアリババの?記事は、言及したCPU集約型のタスクスレッドプールのサイズ、その上にCPUコアを構成しますが、CPUの使用率*(1 +スレッド待機時間/スレッドCPUの** CPU番号IO集約型、スレッドプールのサイズ時間)** OK。CompletableFutureアプリケーションシナリオIO集約型の作業なので、最高のパフォーマンスを達成するために、デフォルトForkJoinPoolは一般的にできない、我々はビジネスをもとに、独自のスレッドプールを作成する必要があります

最終附属書:サンプルコード、歓迎フォークスターを

添付の過去の記事:あなたが読むことを歓迎している、親指、コメント

同時は、関連する
エグゼキュータは、スレッドプールを作成無効にするには1.なぜアリババの?
2.自分のこと、例外処理スレッドを行います

デザインパターン関連:
1.シングルトン、あなたが本当にそれを書くのか?
2.(+工場モデル戦略モード+マップ)キルプロジェクトのスイッチケースをパッケージ化

JAVA8関連:
1.ストリームAPI最適化されたコード
のプロ2、あなたがLocalDateTimeをああ日の代わりに使用することをお勧めします

データベース関連:
クエリ効率1. mysqlデータベース時間のdatetime型、BIGINT、タイムスタンプ比較
2.'m嬉しいです!最後にゆっくりとピットクエリを踏ん

効率的な関連:
1のラインとJavaの足場、統一されたプロジェクトチームの構造のスタイル

ログイン関連:
1.ログフレームは、LogbackまたはLog4j2を選択しますか?
2. Logbackプロファイルそう書かれ、10回TPS

プロジェクトが関連する:
1.何もない、ハンズオンLRUローカルキャッシュを書い
2のRedisは親指の機能モジュール達成
3. JMX視覚的な監視スレッドプール
4.著作権管理[SpringSecurity記事]
マスターへのエントリから5.春のカスタムアノテーションを
6 。Javaは着陸Youkuのシミュレートされた
高いので、それはマルチレベルキャッシュバー書くことであろう7 QPSを
スクリーンショットphantomjsを使用して8 javaの

その他:
1.のtry-と資源優雅近いリソース
2.ボス、なぜべきである私の賃金控除フロートとストレージの量

おすすめ

転載: juejin.im/post/5dfb5bc951882512420b06c5