Java 11 HTTP client asynchronous execution

manouti :

I'm trying the new HTTP client API from JDK 11, specifically its asynchronous way of executing requests. But there is something that I'm not sure I understand (sort of an implementation aspect). In the documentation, it says:

Asynchronous tasks and dependent actions of returned CompletableFuture instances are executed on the threads supplied by the client's Executor, where practical.

As I understand this, it means that if I set a custom executor when creating the HttpClient object:

ExecutorService executor = Executors.newFixedThreadPool(3);

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

then if I send a request asynchronously and add dependent actions on the returned CompletableFuture, the dependent action should execute on the specified executor.

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

However, in the dependent action above (the consumer in thenAccept), I see that the thread doing it is from the common pool and not the custom executor, since it prints Thread is: ForkJoinPool.commonPool-worker-5.

Is this a bug in the implementation? Or something I'm missing? I notice it says "instances are executed on the threads supplied by the client's Executor, where practical", so is this a case where this is not applied?

Note that I also tried thenAcceptAsync as well and it's the same result.

manouti :

I just found an updated documentation (the one I initially linked to seems old) where it explains this implementation behavior:

In general, asynchronous tasks execute in either the thread invoking the operation, e.g. sending an HTTP request, or by the threads supplied by the client's executor. Dependent tasks, those that are triggered by returned CompletionStages or CompletableFutures, that do not explicitly specify an executor, execute in the same default executor as that of CompletableFuture, or the invoking thread if the operation completes before the dependent task is registered.

And the default executor of CompletableFuture is the common pool.

I also found the bug ID that introduces this behavior, in which the API developers fully explains it:

2) Dependent tasks run in the common pool The default execution of dependent tasks has been updated to run in the same executor as that of CompletableFuture's defaultExecutor. This is more familiar to developers that already use CF, and reduces the likelihood of the HTTP Client being starved of threads to execute its tasks. This is just default behaviour, both the HTTP Client and CompletableFuture allow more fine-grain control, if needed.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=36143&siteId=1