非同期リクエスト
sendAsync と CompletableFuture です。
「非同期リクエスト」はCoddyKit上の無料Java Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはJava Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Java Academyコースには全4レッスンが含まれています。
非同期リクエスト
HTTP呼び出しのたびにブロックすると、スレッドを無駄に消費します。JDK HTTPクライアントには sendAsync があり、すぐに CompletableFuture を返して、バックグラウンドのエグゼキューターで処理を実行します。
これにより、多数のリクエストを同時に開始し、スレッドを手動で管理せずに結果を組み合わせられます。
send と sendAsync の比較
戻り値が異なります。
- send は
HttpResponse<T>を返してブロックし、IOExceptionとInterruptedExceptionを宣言します。 - sendAsync は
CompletableFuture<HttpResponse<T>>を返し、ブロックしません。エラーはfutureを通じて通知されます。
基本的な sendAsync の呼び出し
sendAsync は、send と同じリクエストと BodyHandler を受け取ります。返されたfutureは、レスポンスの準備が整うと完了します。
import java.net.URI;
import java.net.http.*;
import java.util.concurrent.CompletableFuture;
public class Main {
public static void main(String[] args) {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://example.com"))
.build();
CompletableFuture<HttpResponse<String>> future =
client.sendAsync(request, HttpResponse.BodyHandlers.ofString());
System.out.println("Request sent, future created");
}
}thenApply による変換
thenApply を使うと、結果の到着後に変換できます。たとえば、レスポンスからボディを取り出せます。
これは、前のステージを完了させたスレッド上で同期的に実行されます。
import java.util.concurrent.CompletableFuture;
public class Main {
public static void main(String[] args) throws Exception {
CompletableFuture<String> future =
CompletableFuture.completedFuture("OK 200")
.thenApply(body -> "Length=" + body.length());
System.out.println(future.get());
}
}thenAccept による消費
副作用だけを実行し、戻り値が不要な場合は thenAccept を使います。Consumer を受け取り、CompletableFuture<Void> を返します。
import java.util.concurrent.CompletableFuture;
public class Main {
public static void main(String[] args) throws Exception {
CompletableFuture<Void> done =
CompletableFuture.completedFuture("payload")
.thenAccept(b -> System.out.println("Received: " + b));
done.get();
}
}join によるブロッキング
main から非同期の結果を待つには、join()(非検査)または get()(検査あり)を呼び出します。実際のサーバーでは、ブロックする代わりにステージを連結します。
join() は失敗を非検査例外の CompletionException でラップします。
import java.util.concurrent.CompletableFuture;
public class Main {
public static void main(String[] args) {
CompletableFuture<Integer> f =
CompletableFuture.supplyAsync(() -> 21).thenApply(n -> n * 2);
int result = f.join();
System.out.println("Result: " + result);
}
}exceptionally によるエラー処理
exceptionally は、ステージが失敗したときに代替値を提供し、処理パイプラインを復旧します。
これにより、ネットワークエラーで非同期HTTP処理全体が停止するのを防げます。
import java.util.concurrent.CompletableFuture;
public class Main {
public static void main(String[] args) {
CompletableFuture<String> f = CompletableFuture
.<String>supplyAsync(() -> { throw new RuntimeException("boom"); })
.exceptionally(ex -> "fallback: " + ex.getMessage());
System.out.println(f.join());
}
}2つのリクエストの結合
thenCombine は、独立した2つのfutureを待って結果を統合します。非同期HTTPでは、2つのエンドポイントを並列に取得してボディを結合できます。
import java.util.concurrent.CompletableFuture;
public class Main {
public static void main(String[] args) {
CompletableFuture<String> a = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> b = CompletableFuture.supplyAsync(() -> "World");
String combined = a.thenCombine(b, (x, y) -> x + " " + y).join();
System.out.println(combined);
}
}多数のリクエストの並列実行
リクエストのバッチを実行するには、それぞれのfutureをリストに集めてから、CompletableFuture.allOf(...) ですべての完了を待ちます。
allOf は CompletableFuture<Void> を返すため、個々の結果は後から読み取ります。
import java.util.*;
import java.util.concurrent.CompletableFuture;
public class Main {
public static void main(String[] args) {
List<CompletableFuture<Integer>> futures = new ArrayList<>();
for (int i = 1; i <= 3; i++) {
int n = i;
futures.add(CompletableFuture.supplyAsync(() -> n * n));
}
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
for (var f : futures) System.out.println(f.join());
}
}非同期パターンのまとめ
一般的な非同期HTTPの処理パイプラインは次のようになります。
client.sendAsync(req, BodyHandlers.ofString())- ボディを取得するための
.thenApply(HttpResponse::body) - ボディを消費するための
.thenAccept(System.out::println) - 復旧するための
.exceptionally(ex -> ...)
ネットワークを待つためにスレッドがブロックされることはありません。
非同期を選ぶ場面
多数の呼び出しを並行して実行する場合、複数のサービスに処理を分散する場合、またはリアクティブ/ノンブロッキングサーバー内で実行する場合は sendAsync を選びます。ブロッキングしても問題ない単純なスクリプトや単発の呼び出しでは send を選びます。
理解度チェック
非同期リクエストについての理解度を確認しましょう。
まとめ
ノンブロッキングHTTP呼び出しの方法を学びました。
- sendAsync はブロックする代わりに
CompletableFutureを返します。 - thenApply / thenAccept で結果を変換し、消費します。
- exceptionally は失敗時の代替値を提供します。
- thenCombine は2つの並列呼び出しを結合し、allOf はバッチ全体の完了を待ちます。
- 非同期処理は高並行性の環境やリアクティブサーバーに適しており、同期処理は単純なスクリプトに適しています。
よくある質問
「非同期リクエスト」レッスンは無料ですか?
はい。「非同期リクエスト」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Java Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Java Academyコースには全4レッスンが含まれています。
「非同期リクエスト」で何を学びますか?
sendAsync と CompletableFuture です。 ブラウザで直接実行するハンズオンコードでJava Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Java Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのJava Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。
「非同期リクエスト」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このJava Academyレッスンでコードを書いて実行できますか?
はい。すべてのJava Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。