java.util.concurrent.CompletableFutureの例外の伝播

アンドレイSukhovitsky:

コードの2つのスニペットがあります。

最初の1で私たちは常にいくつかの例外をスローするタスクからCompletableFutureを作成します。その後、我々はこの未来、そして「theAccept」方法に「非常に」方法を適用します。私たちは、任意の変数にtheAcceptメソッドによって返された新しい未来を割り当てないでください。その後、我々は、元の未来に「参加」を呼び出します。私たちが見ることは「非常に」方法は「thenAccept」と同様に呼び出されていることです。彼らは出力に該当する行を印刷ので、我々はそれを参照してください。しかし、例外は「例外的」方法により抑制されていません。抑止例外ではなく、私たちはこのような場合には「例外的」から期待まさにあるいくつかのデフォルト値を提供してくれると。

第二のスニペットでは、変数にし、その上に「参加」たinvokeはほぼ同じですが、アサイン新しい返さ将来を行います。この場合に予想される例外が抑制されます。

一貫性のある行動が抑制例外のいずれかではなく、「非常に」起動し、「thenAccept」または例外的と抑制例外呼び出すことではない最初の部分のために私の視点から。

なぜ我々は、間に何かを持っているのですか?

まずスニペット:

public class TestClass {
    public static void main(String[] args) {
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(TestClass::doSomethingForInteger);

        future.exceptionally(e -> {
                    System.out.println("Exceptionally");
                    return 42;
                })
                .thenAccept(r -> {
                    System.out.println("Accept");
                });

        future.join();
    }

    private static int doSomethingForInteger() {
        throw new IllegalArgumentException("Error");
    }
}

第二のスニペット:

public class TestClass {
    public static void main(String[] args) {
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(TestClass::doSomethingForInteger);

        CompletableFuture<Void> voidCompletableFuture = future.exceptionally(e -> {
            System.out.println("Exceptionally");
            return 42;
        })
                .thenAccept(r -> {
                    System.out.println("Accept");
                });

        voidCompletableFuture.join();
    }

    private static int doSomethingForInteger() {
        throw new IllegalArgumentException("Error");
    }
}
ホルガー:

「例外を抑制すること」のようなものはありません。あなたが起動するとexceptionally、あなたは、前のステージの結果または前のステージが非常に完了した場合に関数を評価した結果で完了される新しい未来を、作成しています。前段は、つまりは、あなたが呼び出している未来exceptionallyには、影響を受けません。

これは、に適用されるすべての機能またはアクションに依存連鎖する方法。これらの各メソッドは、文書として完成される新しい未来を、作成します。それらのどれもあなたがメソッドを呼び出している既存の将来に影響を与えません。

おそらく、それは次の例と非常に明確になります:

CompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> {
    LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
    return "a string";
});

CompletableFuture<Integer> f2 = f1.thenApply(s -> {
    LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(2));
    return s.length();
});

f2.thenAccept(i -> System.out.println("result of f2 = "+i));

String s = f1.join();
System.out.println("result of f1 = "+s);

ForkJoinPool.commonPool().awaitQuiescence(1, TimeUnit.DAYS);

ここでは、依存ステージの結果は、ことを明確にする必要がありInteger、前提条件の段階の結果に取って代わることはできませんStringこれらは、単純に異なる結果を持つ2つの異なる先物です。そして、呼び出し以降join()f1第一段階の結果の照会、それはに依存しないf2、したがって、でもその完了を待ちません。(つまり、最後に、すべてのバックグラウンド活性の端のための理由コードを待つ理由です)。

使用量はexceptionally異なるものではありません。次の段階は、非例外的なケースでは、同じタイプとでも同じ結果を持っていることを混乱されるかもしれないが、それは二つの別個の段階があるという事実は変わりません。

static void report(String s, CompletableFuture<?> f) {
    f.whenComplete((i,t) -> {
        if(t != null) System.out.println(s+" completed exceptionally with "+t);
        else System.out.println(s+" completed with value "+i);
    });
}
CompletableFuture<Integer> f1 = CompletableFuture.supplyAsync(() -> {
    LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
    throw new IllegalArgumentException("Error for testing");
});
CompletableFuture<Integer> f2 = f1.exceptionally(t -> 42);

report("f1", f1);
report("f2", f2);

ForkJoinPool.commonPool().awaitQuiescence(1, TimeUnit.DAYS);

広範な考え方があるように思わCompletableFuture連鎖する方法は残念ながら誤解を招く間違っている、単一の将来のためのビルダーのいくつかの種類であることを。もう一つの落とし穴は、以下の間違いです。

CompletableFuture<?> f = CompletableFuture.supplyAsync(() -> {
    LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
    System.out.println("initial stage");
    return "";
}).thenApply(s -> {
    LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
    System.out.println("second stage");
    return s;
}).thenApply(s -> {
    LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
    System.out.println("third stage");
    return s;
}).thenAccept(s -> {
    System.out.println("last stage");
});

f.cancel(true);
report("f", f);

ForkJoinPool.commonPool().awaitQuiescence(1, TimeUnit.DAYS);

ASは、各連鎖方式がそう最後の連鎖メソッドによって返される段階、すなわち最終ステージへの参照を保持し、新たなステージを作成し、説明し、最終的な結果を得るのに適しています。しかし、この段階をキャンセルすることだけが、最後のステージと前提条件の段階のどれもがキャンセルされます。それはすでに解約により終了し、その他、今の無関係の段階がまだバックグラウンドで評価されている間、この並外れた結果を報告することのできるようまた、キャンセルした後、最後のステージは、もはや他のステージに依存しません。

おすすめ

転載: http://43.154.161.224:23101/article/api/json?id=208531&siteId=1