web-dev-qa-db-ja.com

Java 11HTTPクライアントの非同期実行

JDK 11の新しいHTTPクライアントAPI、特にリクエストを実行する非同期の方法を試しています。しかし、私が理解できるかどうかわからないことがあります(実装の側面のようなものです)。 documentation には、次のように書かれています。

返されたCompletableFutureインスタンスの非同期タスクと依存アクションは、実用的な場合、クライアントのExecutorによって提供されるスレッドで実行されます。

これを理解しているので、HttpClientオブジェクトを作成するときにカスタムエグゼキュータを設定すると、次のようになります。

ExecutorService executor = Executors.newFixedThreadPool(3);

HttpClient httpClient = HttpClient.newBuilder()
                      .executor(executor)  // custom executor
                      .build();

次に、リクエストを非同期で送信し、返されたCompletableFutureに依存アクションを追加すると、依存アクションは指定されたエグゼキュータで実行されるはずです。

httpClient.sendAsync(request, BodyHandlers.ofString())
          .thenAccept(response -> {
      System.out.println("Thread is: " + Thread.currentThread().getName());
      // do something when the response is received
});

ただし、上記の依存アクション(thenAcceptのコンシューマー)では、Thread is: ForkJoinPool.commonPool-worker-5を出力するため、それを実行するスレッドはカスタムエグゼキューターではなく共通プールからのものであることがわかります。

これは実装のバグですか?または私が欠けている何か? 「インスタンスはクライアントのエグゼキュータによって提供されたスレッドで実行されます実用的な場合」と書かれているのに気づきましたが、これは適用されない場合ですか?

thenAcceptAsyncも試しましたが、同じ結果であることに注意してください。

13
M Anouti

更新された ドキュメント (最初にリンクしたものは古いようです)を見つけました。ここで、この実装動作を説明しています。

一般に、非同期タスクは、操作を呼び出すスレッドのいずれかで実行されます。 送信 HTTPリクエスト、またはクライアントの エグゼキュータ によって提供されるスレッドによって。 エグゼキュータを明示的に指定しない、返されたCompletionStagesまたはCompletableFuturesによってトリガーされる依存タスクは、CompletableFuture、または依存タスクが登録される前に操作が完了した場合は呼び出しスレッド。

また、CompletableFutureのデフォルトのエグゼキュータは共通プールです。

また、この動作を導入する バグID も見つかりました。ここでは、API開発者が完全に説明しています。

2)共通プールで実行される依存タスク依存タスクのデフォルトの実行が更新され、CompletableFutureのdefaultExecutorと同じエグゼキューターで実行されるようになりました。これは、すでにCFを使用している開発者にとってはなじみがあり、HTTPクライアントがタスクを実行するためにスレッドが不足する可能性を減らします。これは単なるデフォルトの動作であり、HTTPクライアントとCompletableFutureの両方で、必要に応じてよりきめ細かい制御が可能です。

10
manouti

ショートバージョン:実装の詳細を特定したと思います。「wherepractical」は、提供されたexecutorが保証されないことを意味します。利用される。

詳細に:

ここ からJDK11ソースをダウンロードしました。 (この記事の執筆時点ではjdk11-f729ca27cf9a)。

src/Java.net.http/share/classes/jdk/internal/net/http/HttpClientImpl.Javaには、次のクラスがあります。

/**
 * A DelegatingExecutor is an executor that delegates tasks to
 * a wrapped executor when it detects that the current thread
 * is the SelectorManager thread. If the current thread is not
 * the selector manager thread the given task is executed inline.
 */
final static class DelegatingExecutor implements Executor {

このクラスは、executorがtrueの場合はisInSelectorThreadを使用し、それ以外の場合はタスクをインラインで実行します。これは要約すると次のようになります。

boolean isSelectorThread() {
    return Thread.currentThread() == selmgr;
}

ここで、selmgrSelectorManagerです。 編集:このクラスはHttpClientImpl.Javaにも含まれています:

// Main loop for this client's selector
private final static class SelectorManager extends Thread {

結論:実用的は実装に依存し、提供されたexecutorが使用される保証がないことを意味すると推測しています。

注:これは、ビルダーがexecutorを提供しないデフォルトのエグゼキューターとは異なります。その場合、コードは明らかに新しいキャッシュスレッドプールを作成します。別の言い方をすれば、ビルダーがexecutorを提供する場合、SelectorManagerのIDチェックが行われます。

8
Michael Easter