java.util.concurrent.CompletableFutureクラスメソッドのガイド

Java8で導入されたCompletableFutureクラスは、実行の並列スレッド間で情報を転送するためのツールです。 基本的に、1つの参照値のみを渡すことができるブロッキングキューです。 通常のキューとは異なり、送信された値の計算中に例外が発生した場合も例外をスローします。

クラスには、迷子になりやすい数十のメソッドが含まれています。 この記事では、これらのメソッドをいくつかの基準に従って分類し、簡単にナビゲートできるようにします。

ウォームアップのために、多くのメソッドでパラメーター型として使用されるjava.util.Functionパッケージの新しいインターフェースを導入します。

//  ,   BiFunction<T, U,R> { R apply(T t, U u); } //  ,    BiConsumer<T,U> { void accept(T t, U u) } //  ,   Function<T, R> { R apply(T t); } //  ,    Consumer<T> { void accept(T t); } //  ,   Supplier<T> { T get(); } 

古き良きRunnableも思い出してください:
 //  ,    Runnable { void run(); } 

これらのインターフェイスは機能的です。つまり、この型の値は、オブジェクトへの参照と、メソッドまたはラムダ式への参照の両方で設定できます。

CompletableFutureクラスには、データを転送する手段として、書き込み用と読み取り用の2つのサブインターフェースがあり、これらは直接(同期)と間接(非同期)に分かれています。 直読サブインターフェース(java 5の時代から存在していたjava.util.concurrent.Future )のみがプログラムで強調表示されますが、分類のために、残りを精神的に分離することが有用です。 このサブインターフェースの分離に加えて、基本的なメソッドと特定のケースを実装するメソッドの分離も試みます。

簡潔にするために、「CompletableFuture型のオブジェクト」の代わりに「futures」と言います。 「この先物」とは、説明した方法が適用される先物を意味します。

1.直接記録インターフェース


もちろん、2つの基本的な方法があります-値を書き、例外を書きます:
 boolean complete(T value) boolean completeExceptionally(Throwable ex) 
明らかなセマンティクスで。

その他の方法:

 boolean cancel(boolean mayInterruptIfRunning) 
completeExceptionally(new CancellationException)と同等です。 java.util.concurrent.Futureとの互換性のために導入されました。

 static <U> CompletableFuture<U> completedFuture(U value) 
CompletableFuture res=new CompletableFuture(); res.complete(value)と同等 CompletableFuture res=new CompletableFuture(); res.complete(value)

 void obtrudeValue(T value) void obtrudeException(Throwable ex) 
保存された値を強制的に上書きします。 自分の足を撃つ確実な方法。

2.直読インターフェイス


 boolean isDone() 
結果がこの先物にすでに書き込まれているかどうかを確認します。

 T get() 
結果がまだ書き込まれていない場合は待機し、値を返します。 例外がスローされた場合、ExecutionExceptionをスローします。

その他の方法:

 boolean isCancelled() 
cancel()メソッドを使用して例外が記録されたかどうかを確認します。

 T join() 
get()と同じですが、CompletionExceptionをスローします。

 T get(long timeout, TimeUnit unit) 
タイムアウト付きでget()get()ます。

 T getNow(T valueIfAbsent) 
結果をすぐに返します。 結果がまだ書き込まれていない場合、 valueIfAbsentパラメーターの値を返します。

 int getNumberOfDependents() 
これを満たすのを待っている他のCompletableFutureのおおよその数。

3.間接記録インターフェース


 static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) 
サプライヤ機能を持つタスクが起動され、実行結果が先物に書き込まれます。 タスクは標準のスレッドプールで起動されます。

 static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor) 
同じことですが、executorパラメーターで指定されたスレッドプールで開始します。

 static CompletableFuture<Void> runAsync(Runnable runnable) static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor) 
supplyAsyncと同じsupplyAsyncが、ストックのタイプはRunnableであるため、結果はVoidタイプになります。

4.仲介された読み取りインターフェイス


この(および/または他の)先物を記入するとすぐに特定のアクション(反応)を実行するように規定します。 最も広範なサブインターフェース。 2つの兆候に従ってコンポーネントを分類します。

a)充填反応を開始する方法:先物を充填するときにメソッドとして同期的に実行することも、スレッドプールのタスクとして非同期的に実行することもできます。 非同期起動の場合、接尾辞Asyncのメソッドが使用されます(2つのバージョンでは、共通スレッドForkJoinPool.commonPool() 、または追加のパラメーターで指定されたストリームで起動します)。 同期的に実行する方法のみを以下に説明します。

b)この先物とその充填に対する反応との関係のトポロジー:線形、「any」と入力し、「all」と入力します。

-線形関係:1つの先物が反応に1つの値を提供します

-「任意の」方法-入力での2つ以上の先物。 先物の1つに現れる最初の(時間内の)結果が反応に転送されます。 他の結果は無視されます

-「すべて」の方法-入力での2つ以上の先物。 すべての先物の結果が蓄積され、反応に転送されます。

4.1この先物を満たすための反応を実行する(線形関係)

これらのメソッドの名前は接頭辞thenで始まり、1つのパラメーター(反応)があり、 CompletableFuture型の新しいフューチャーを返し、反応の結果にアクセスします。 それらは反応のタイプが異なります。

 <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn) 
反応が特定のフューチャーから値を取得し、戻り値が結果のフューチャーに渡されるメインメソッド。

 CompletableFuture<Void> thenAccept(Consumer<? super T> block) 
反応は、指定されたフューチャーから値を取得しますが、値を返しません。
結果の先物の値はVoid型です。

 CompletableFuture<Void> thenRun(Runnable action) 
反応は値を受け取ったり返したりしません。

compute1..compute4をメソッド参照とします。 ステップからステップへの値の転送を伴う線形チェーンは、次のようになります。
 supplyAsync(compute1) .thenApply(compute2) .thenApply(compute3) .thenAccept(compute4); 

これは単純な呼び出しと同等です
 compute4(compute3(compute2(compute1()))); 

 <U> CompletableFuture<U> thenCompose(Function<? super T, CompletableFuture<U>> fn) 
thenApplyと同じthenApplyが、リアクション自体はレディ値ではなくフューチャーを返します。 これは、複雑なトポロジの反応を使用する必要がある場合に必要になることがあります。

4.2多くの先物のいずれかを埋めるための反応を実行する

 static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs) 
cfsパラメーターで渡された先物のいずれかが満たされると満たされる新しい先物を返します。 結果は、完了した先物の結果と一致します。

4.3 2つの先物のいずれかを入力するリアクションを実行する

主な方法:

 <U> CompletableFuture<U> applyToEither(CompletableFuture<? extends T> other, Function<? super T,U> fn) 
この先物またはotherパラメーターで指定された先物が満たされたときに満たされる新しい先物を返します。 結果は、完了した先物の結果と一致します。

メソッドは式と同等です:
 CompletableFuture.anyOf(this, other).thenApply(fn); 

他の2つの方法は、反応のタイプのみが異なります。

 CompletableFuture<Void> acceptEither(CompletableFuture<? extends T> other, Consumer<? super T> block) CompletableFuture<Void> runAfterEither(CompletableFuture<?> other, Runnable action) 

十分な場合に3 *いずれかの方法(9を考慮に入れて*非同期オプション)を行う必要がある理由は明らかではありません。
 <T> CompletableFuture<T> either(CompletableFuture<? extends T> other) { return CompletableFuture.anyOf(this, other); } 

これらのメソッドはすべて次のように表現できます

 f1.applyToEither(other, fn) == f1.either(other).thenApply(fn); f1.applyToEitherAsync(other, fn) == f1.either(other).thenApplyAsync(fn); f1.applyToEitherAsync(other, fn, executor) == f1.either(other).thenApplyAsync(fn, executor); f1.acceptEither(other, block) == f1.either(other).thenAccept(other); f1.runAfterEither(other, action) == f1.either(other).thenRun(action); 

など さらに、いずれかを他の組み合わせで使用することもできます。

4.4 2つの先物を埋めるためのリアクションを実行する

 <U,V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn) 
主な方法。 入力には2つの先物があり、その結果は蓄積され、2つのパラメーターの関数である反応に転送されます。

他の方法は、反応のタイプが異なります。

 <U> CompletableFuture<Void> thenAcceptBoth(CompletableFuture<? extends U> other, BiConsumer<? super T,? super U> block) 
反応は値を返しません

 CompletableFuture<Void> runAfterBoth(CompletableFuture<?> other, Runnable action) 
反応はパラメーターをとらず、値を返しません

4.5多くの先物を埋めるための反応を実行する

 static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs) 
パラメーターリストのすべての先物が完了したときに完了するCompletableFutureを返します。 この方法の明らかな欠点は、先物パラメータで取得した値が結果の先物に転送されないため、必要な場合は他の方法で転送する必要があることです。

4.6。 実行時エラーの傍受

ある段階で先物が異常終了した場合、例外は先物チェーンに渡されます。 エラーに応答して通常の実行に戻るには、例外をキャッチする方法を使用できます。

 CompletableFuture<T> exceptionally(Function<Throwable,? extends T> fn) 
この先物が異常終了した場合、結果の先物はfn関数によって生成された結果で終了します。 この先物が正常に終了した場合、結果の先物は同じ結果で正常に終了します。

 <U> CompletableFuture<U> handle(BiFunction<? super T,Throwable,? extends U> fn) 
このメソッドでは、先物が正常に終了したか異常に終了したかに関係なく、反応が常に呼び出されます。 フューチャーが結果rで正常に終了した場合、パラメーター(r, null)はリアクションに転送され、例外exで失敗した場合、パラメーター(null, ex)はリアクションに転送されます。 反応の結果は、この先物の結果とは異なるタイプのものである可能性があります。

次の例は、 http//nurkiewicz.blogspot.ru/2013/05/java-8-definitive-guide-to.htmlから取得したものです。

 CompletableFuture<Integer> safe = future.handle((r, ex) -> { if (r != null) { return Integer.parseInt(r); } else { log.warn("Problem", ex); return -1; } }); 

ここで、 futureString型またはエラーの結果を生成し、反応は結果を整数に変換し、エラーの場合は-1を返します。 一般に、検証はif (ex!=null)で始まる必要があることに注意してくださいr==nullは異常終了時と正常終了時の両方になりますが、この例ではr==nullの場合はエラーr==null見なされます。

特定の問題を解決するための提案の形で関心が示されている場合、継続があります。

Source: https://habr.com/ru/post/J213319/


All Articles