Java 8 の CompletableFuture についての深い理解

CompletableFuture は、Java 8 で導入された新しい同時プログラミング ツールで、開発者に非同期操作と同時タスクを処理するためのシンプルかつ効率的な方法を提供します。CompletableFuture は、Future の機能を強化し、より豊富な機能とより便利な使い方を提供するものと言えます。

このチュートリアルでは、CompletableFuture の原理、アプリケーション、サンプル コードを学び、CompletableFuture の使い方をよりよく理解し、使いこなすことができます。

1. CompletableFuture の原則

1.1 CompletableFuture の基本コンセプト

CompletableFuture の原理を紹介する前に、CompletableFuture の基本概念をいくつか理解しましょう。

CompletableFuture は、Future インターフェイスを実装するクラスであり、非同期コンピューティング タスクの結果を表すことができます。CompletableFuture は計算を同期または非同期で完了でき、呼び出しの連鎖、複数の CompletableFuture の構成とマージをサポートします。

CompletableFuture では、thenApply、thenAccept、thenRun、thenCompose などのメソッドを使用して複数の CompletableFuture を直列に接続し、複数の非同期タスク間の依存関係を実現できます。さらに、CompletableFuture は、2 つの非同期タスク間のコラボレーションを容易にするために、thenCombine、thenAccept Both、runAfter Both などのメソッドも提供します。

CompletableFuture は、complete、completeExceptionally、cancel などのメソッドを通じて手動で計算タスクを完了でき、get、join、getNow、getTimeout などのメソッドを通じて計算結果を取得することもできます。

1.2 CompletableFuture の実行モデル

CompletableFuture の実行モデルは主に同期実行と非同期実行の 2 種類に分かれます。

  • 同期実行: 同期実行とは、現在のスレッドが CompletableFuture の計算結果を待つことを意味します。CompletableFuture の計算が完了すると、現在のスレッドは直ちに実行を継続します。CompletableFuture の計算がまだ完了していない場合、現在のスレッドは計算が完了するまでブロックされます。
  • 非同期実行: 非同期実行とは、現在のスレッドが CompletableFuture の計算結果を待つ必要がなく、実行を継続することを意味します。CompletableFuture の計算が完了すると、呼び出し元のスレッドに通知が届き、計算の結果を処理できるようになります。

CompletableFuture の非同期実行は主に ForkJoinPool を通じて実現されます。ForkJoinPool は Java 7 の新しいスレッド プールです。タスクを複数のサブタスクに分割して並列実行できるワークスチール モデルのスレッド プールです。CompletableFuture では、Executor が明示的に指定されていない場合、CompletableFuture はデフォルトで ForkJoinPool を使用して非同期実行を実装します。

1.3 CompletableFutureの状態遷移

CompletableFuture には、不完全、完了、異常完了、キャンセルの 4 つの状態があります。CompletableFuture の状態が変化すると、CompletionStage の関連するコールバック メソッドがトリガーされ、呼び出し元のスレッドが計算結果を処理できるようになります。

CompletableFuture の 4 つの状態は次のとおりです。

  • Incomplete: CompletableFuture の計算は完了しておらず、現時点では CompletableFuture は不完全な状態です。
  • Completed: CompletableFuture の計算が完了し、結果が正常に返されました。この時点で、CompletableFuture は完了した状態になります。
  • 異常完了: CompletableFuture の計算は完了しましたが、例外が発生しました。この時点で、CompletableFuture は異常に完了した状態になります。
  • キャンセル: CompletableFuture の計算はキャンセルされました。この時点で、CompletableFuture はキャンセルされた状態になります。

CompletableFuture の状態遷移は、complete、completeExceptionally、cancel などのメソッドによってトリガーできます。以下は CompletableFuture の状態遷移図です。

+-------------------------+
         |          NEW            |
         +-------------------------+
                    |
                    v
         +-------------------------+
         |        COMPLETING       |
         +-------------------------+
           /                   \
          /                     \
         /                       \
        /                         \
       /                           \
      v                             v
+-------------------------+  +-------------------------+
|       NORMAL VALUE      |  |       EXCEPTIONAL        |
+-------------------------+  +-------------------------+

状態遷移図では、CompletableFuture に NEW と COMPLETING という 2 つの中間状態があることがわかります。CompletableFuture オブジェクトを作成すると、その状態は NEW になります。CompletableFuture の計算メソッドを呼び出し、計算が完了していない場合、CompletableFuture の状態は NEW から COMPLETING に変換されます。COMPLETING 状態では、CompletableFuture の結果が確定しておらず、正常な計算結果である場合もあれば、異常な結果である場合もあります。

CompletableFuture の計算が完了すると、その状態は COMPLETING から NORMAL VALUE または EXCEPTIONAL に遷移します。評価の結果が正常であれば、CompletableFuture の状態は NORMAL VALUE に変わります。計算結果が異常な場合、CompletableFuture の状態は EXCEPTIONAL に変わります。

1.4 CompletableFuture の例外処理

CompletableFuture では、例外的に、whenComplete、whenCompleteAsync などのメソッドを介して例外を処理できます。

  • ハンドル: 計算結果または計算完了時の例外を処理できます。
  • 例外的に: 計算が異常な場合に、異常な状況に対処できます。
  • whenComplete および whenCompleteAsync: 現在のスレッドをブロックすることなく、計算が完了したときに計算結果または例外を処理できます。

次に、handle メソッドを使用して例外を処理する例を示します。

CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
    // 模拟一个会抛出异常的计算过程
    throw new RuntimeException("计算过程中出现异常");
});

CompletableFuture<String> handleFuture = future.handle((result, exception) -> {
    if (exception != null) {
        return "计算出现异常:" + exception.getMessage();
    } else {
        return "计算结果:" + result;
    }
});

System.out.println(handleFuture.get()); // 输出:"计算出现异常:计算过程中出现异常"

この例では、例外をスローする計算をシミュレートする CompletableFuture オブジェクト future を作成します。そして、将来の計算結果や例外を handle メソッドで処理し、新しい CompletableFuture オブジェクト handleFuture を生成します。

Future の計算中に例外が発生した場合、handleFuture の計算結果は例外情報を含む文字列になります。それ以外の場合、handleFuture は評価結果を含む文字列として評価されます。

2. CompletableFutureの応用

2.1 非同期呼び出し

CompletableFuture の最も一般的な使用法の 1 つは、非同期呼び出しを行うことです。CompletableFuture.supplyAsync や CompletableFuture.runAsync などのメソッドを使用して、計算処理を非同期に実行し、計算結果を取得したり、計算完了後に後続の処理を実行したりできます。

CompletableFuture.supplyAsync メソッドを使用して計算を非同期的に呼び出す例を次に示します。

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    try {
        // 模拟一个耗时的计算过程
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "Hello, CompletableFuture!";
});

// 等待计算完成,并获取计算结果
String result = future.get();

System.out.println(result); // 输出:"Hello, CompletableFuture!"

この例では、CompletableFuture.supplyAsync メソッドを使用して、時間のかかる操作をシミュレートして文字列を返す計算を非同期的に実行します。future.get() メソッドを使用して、計算が完了するのを待ち、計算結果を取得します。

2.2 組み合わせ計算

CompletableFuture のもう 1 つの非常に強力な用途は、計算を作成することです。thenCompose、thenApply、thenAccept、thenRun、thenCombine、thenAccept Both などのメソッドを使用して、複数の CompletableFuture オブジェクトを結合して計算し、計算結果を取得したり、計算完了後に後続の処理を実行したりできます。

  • thenCompose と thenApply: 2 つの CompletableFuture オブジェクトを結合して計算し、新しい CompletableFuture オブジェクトを返すことができます。
  • thenAccept と thenRun: タスクは CompletableFuture オブジェクトの計算が完了した後に実行できますが、結果は返されません。
  • thenCombine および thenAccept Both: 2 つの CompletableFuture オブジェクトの計算結果を結合して計算し、新しい CompletableFuture オブジェクトを返すか、タスクを実行できます。

thenCompose メソッドと thenApply メソッドを使用して計算を作成する例を次に示します。

CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
    try {
        // 模拟一个耗时的计算过程
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "Hello,";
});

CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
    try {
        // 模拟一个耗时的计算过程
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "CompletableFuture!";
});

CompletableFuture<String> future3 = future1.thenCompose(result1 -> {
    return future2.thenApply(result2 -> {
        return result1 + " " + result2;
    });
});

// 等待计算完成,并获取计算结果
String result = future3.get();

System.out.println(result); // 输出:"Hello, CompletableFuture!"

この例では、CompletableFuture.supplyAsync メソッドを使用して 2 つの計算を非同期に実行し、それぞれ文字列「Hello」と「CompletableFuture!」を返します。次に、thenCompose メソッドと thenApply メソッドを使用して、これら 2 つの CompletableFuture オブジェクトを計算用に結合し、新しい CompletableFuture オブジェクト future3 を生成します。この計算中に、2 つの文字列を連結して新しい文字列を返します。最後に、future3.get() メソッドを使用して計算が完了するのを待ち、計算結果を取得します。

2.3 例外処理

CompletableFuture は、計算中に発生する例外の処理もサポートします。Exceptly、handle、whenComplete、whenCompleteAsync などのメソッドを使用して、例外を処理し、対応するフォローアップ処理を実行できます。

以下は、Exceptionally メソッドを使用して例外を処理する例です。

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    // 抛出一个异常
    throw new RuntimeException("计算出现异常!");
});

CompletableFuture<String> exceptionFuture = future.exceptionally(ex -> {
    // 处理异常并返回一个默认值
    return "计算出现异常,返回默认值。";
});

// 等待计算完成,并获取计算结果
String result = exceptionFuture.get();

System.out.println(result); // 输出:"计算出现异常,返回默认值。"

この例では、CompletableFuture.supplyAsync メソッドを使用して計算を非同期に実行し、RuntimeException をスローします。次に、Exceptionally メソッドを使用して例外を処理し、デフォルト値を返します。最後に、ExceptionFuture.get() メソッドを使用して計算が完了するのを待ち、計算結果を取得します。

2.4 複数の非同期操作の結合

CompletableFuture は、計算のために複数の非同期操作を一緒に構成することもサポートしています。thenCombine、thenAccept Both、runAfter Both などのメソッドを使用して、複数の CompletableFuture オブジェクトを結合して計算し、新しい CompletableFuture オブジェクトを返すか、タスクを実行できます。

次に、thenCombine メソッドを使用して複数の非同期操作を結合する例を示します。

CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
    try {
        // 模拟一个耗时的计算过程
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return 100;
});

CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
    try {
        // 模拟一个耗时的计算过程
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return 200;
});

CompletableFuture<Integer> future3 = future1.thenCombine(future2, (result1, result2) -> {
    return result1 + result2;
});

// 等待计算完成,并获取计算结果
Integer result = future3.get();

System.out.println(result); // 输出:300

この例では、CompletableFuture.supplyAsync メソッドを使用して 2 つの計算を非同期に実行し、それぞれ整数 100 と 200 を返します。次に、thenCombine メソッドを使用して 2 つの CompletableFuture オブジェクトを計算用に結合し、新しい CompletableFuture オブジェクト future3 を生成します。この計算中に、2 つの整数を加算し、新しい整数を返します。最後に、future3.get() メソッドを使用して計算が完了するのを待ち、計算結果を取得します。

2.5 その他の方法

上記のメソッドに加えて、CompletableFuture は、allOf、anyOf、layedExecutor、completedFuture など、他の多くの便利なメソッドも提供します。

allOf: すべての CompletableFuture オブジェクトが計算されると、新しい CompletableFuture オブジェクトが返され、このオブジェクトの計算結果は Void 型の値になります。

allOf メソッドを使用して複数の非同期操作を組み合わせる例を次に示します。

CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
    System.out.println("任务 1 开始执行...");
    try {
        // 模拟一个耗时的计算过程
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("任务 1 执行完成。");
});

CompletableFuture<Void> future2 = CompletableFuture.runAsync(() -> {
    System.out.println("任务 2 开始执行...");
    try {
        // 模拟一个耗时的计算过程
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("任务 2 执行完成。");
});

CompletableFuture<Void> allFutures = CompletableFuture.allOf(future1, future2);

// 等待所有的计算完成
allFutures.get();

System.out.println("所有的任务都已经执行完成。");

この例では、CompletableFuture.runAsync メソッドを使用して 2 つのタスクを非同期に実行します。次に、allOf メソッドを使用して、これら 2 つの CompletableFuture オブジェクトを結合し、新しい CompletableFuture オブジェクト allFutures を生成します。この計算プロセスでは、計算操作は実行されず、すべてのタスクが実行されるのを待つだけでした。最後に、allFutures.get() メソッドを使用して、すべての計算が完了するのを待ちます。

anyOf: CompletableFuture オブジェクトが計算されると、新しい CompletableFuture オブジェクトが返され、このオブジェクトの計算結果は Object 型の値になります。

次に、anyOf メソッドを使用して複数の非同期操作を組み合わせる例を示します。

CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
    try {
        // 模拟一个耗时的计算过程
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "Hello";
});

CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
    try {
        // 模拟一个耗时的计算过程
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "World";
});

CompletableFuture<Object> anyFuture = CompletableFuture.anyOf(future1, future2);

// 等待计算完成,并获取计算结果
Object result = anyFuture.get();

System.out.println(result); // 输出:"Hello" 或 "World"

この例では、CompletableFuture.supplyAsync メソッドを使用して 2 つの計算を非同期に実行し、それぞれ文字列 "Hello" と "World" を返します。次に、anyOf メソッドを使用して 2 つの CompletableFuture オブジェクトを結合し、新しい CompletableFuture オブジェクト anyFuture を生成します。この計算プロセスでは、計算が完了するのを待つだけで済みます。最後に、anyFuture.get() メソッドを使用して計算が完了するのを待ち、計算結果を取得します。

lateExecutor: タスクを実行する前に指定された時間遅延させる Executor オブジェクトを返します。

以下に、layedExecutor メソッドを使用したタスクの遅延実行の例を示します。

CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
    System.out.println("任务开始执行...");
}, CompletableFuture.delayedExecutor(1, TimeUnit.SECONDS));

// 等待任务执行完成
future.get();

この例では、CompletableFuture.layedExecutor メソッドを使用して、タスクの実行を 1 秒遅らせる Executor オブジェクトを作成します。次に、この Executor オブジェクトをパラメーターとして指定し、CompletableFuture.runAsync メソッドを使用してタスクを非同期に実行します。最後に、future.get() メソッドを使用してタスクの実行が完了するのを待ちます。

例外的に: 非同期操作の例外を処理し、元の CompletableFuture オブジェクトと同じ値またはデフォルト値に評価される新しい CompletableFuture オブジェクトを返します。

以下に、例外条件を処理するための例外メソッドの使用例を示します。

CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
    // 抛出一个异常
    throw new RuntimeException("计算出现错误");
}).exceptionally(e -> {
    // 处理异常情况
    System.out.println(e.getMessage());
    // 返回默认值
    return 0;
});

// 等待计算完成,并获取计算结果
int result = future.get();

System.out.println("计算结果为:" + result);

この例では、CompletableFuture.supplyAsync メソッドを使用して計算を非同期に実行し、計算中に例外をスローします。例外メソッドを使用してこの例外を処理し、デフォルト値の 0 を返します。最後に、future.get() メソッドを使用して計算が完了するのを待ち、計算結果を取得します。

handle: 非同期操作の結果と例外を処理し、計算結果が元の CompletableFuture オブジェクトとは異なる値である新しい CompletableFuture オブジェクトを返します。

次に、handle メソッドを使用して結果と例外を処理する例を示します。

CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
    // 返回一个整数值
    return 100;
}).handle((result, e) -> {
    if (e != null) {
        // 处理异常情况
        System.out.println(e.getMessage());
        // 返回默认值
        return 0;
    } else {
        // 处理正常情况
        System.out.println("计算结果为:" + result);
        // 返回计算结果的两倍
        return result * 2;
    }
});

// 等待计算完成,并获取计算结果
int result = future.get();

System.out.println("计算结果为:" + result);

この例では、CompletableFuture.supplyAsync メソッドを使用して計算を非同期に実行し、整数値 100 を返します。handle メソッドを使用して計算結果と例外を処理し、計算結果を 2 倍にして返します。最後に、future.get() メソッドを使用して計算が完了するのを待ち、計算結果を取得します。

これまで、CompletableFuture の基本的な使用法と、thenApply、thenAccept、thenRun、thenCompose、thenCombine、例外的に handle、allOf、anyOf などのいくつかの重要なメソッドを紹介しました。実際のアプリケーションでは、さらに CompletableFuture メソッドを使用する可能性があるため、ここでは 1 つずつ紹介しません。CompletableFuture について詳しく知るには、Java 公式ドキュメントやその他の関連リソースを参照してください。

3 つの結論

この記事では、Java 8 の CompletableFuture クラスを紹介します。これは、非同期操作と並列コンピューティングの実現に役立つ便利な API と強力な合成機能を提供する、Future ベースの非同期プログラミング モデルです。最初に、CompletableFuture オブジェクトの作成方法、計算処理を非同期で実行する方法、計算結果を処理するコールバック関数の使用方法など、CompletableFuture の基本的な概念と使い方を紹介しました。次に、CompletableFuture オブジェクトをより柔軟に結合できるように、thenApply、thenAccept、thenRun、thenCompose、thenCombine、例外的に handle、allOf、anyOf などの一般的に使用されるメソッドをいくつか導入しました。最後に、単一タスクの非同期実行、複数のタスクの非同期実行とそれらすべての完了の待機、任意のタスクの完了の待機など、いくつかのサンプル コードを通じて CompletableFuture の使用法を示しました。

CompletableFuture は、非同期操作と並列コンピューティングの複雑さに対処するのに役立つ豊富な API と強力な合成機能を提供する強力な非同期プログラミング モデルであり、同時にコードをより簡潔で理解しやすくします。実際のアプリケーションでは、CompletableFuture を Java 8 の Stream API、Lambda 式、メソッド参照などの新機能と組み合わせて、コードの可読性と保守性をさらに向上させることができます。

CompletableFuture は便利な API と強力な組み合わせ機能を提供しますが、乱用や悪用を避けるために合理的に使用する必要があることに注意してください。実際のアプリケーションでは、スレッド セーフ、例外処理、メモリ リーク、同時実行パフォーマンスなどの潜在的な問題に注意を払う必要があります。特に複雑な非同期演算や並列計算を扱う場合、マルチコアCPUや分散コンピューティングリソースを最大限に活用してプログラムのパフォーマンスを向上させるには、アルゴリズム、データ構造、タスク分割、負荷分散、同時実行制御などを慎重に検討する必要があります。 . そしてスケーラビリティ。

この記事のサンプル コードは、CompletableFuture の基本的な使用方法と一般的なメソッドを示すことのみを目的としており、独自のニーズや実際の状況に応じてこれらのコードを変更および拡張して、より複雑で実用的な非同期操作や並列コンピューティングを実現できます。CompletableFuture の原理と実装の詳細について詳しく知りたい場合は、Java 8 ソース コードとその他の関連リソースを参照して、CompletableFuture の内部実装と最適化戦略、および CompletableFuture を使用して並行プログラミングと非同期操作をより適切に実装する方法について学ぶことができます。 。

つまり、CompletableFuture は非常に便利な非同期プログラミング モデルであり、非同期操作と並列コンピューティングの実現に役立つ豊富な API と強力な合成機能を提供します。実際のアプリケーションでは、自分たちのニーズと実際の状況に応じて適切な方法と戦略を選択し、CompletableFuture を合理的に使用して、コードの品質とパフォーマンスをさらに向上させ、より複雑で実用的なアプリケーションを実現できます。

おすすめ

転載: blog.csdn.net/bairo007/article/details/132294353