20

我正在尝试来自 JDK 11 的新 HTTP 客户端 API,特别是它执行请求的异步方式。但是有些东西我不确定我是否理解(某种实现方面)。在文档中,它说:

在可行的情况下,异步任务和返回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,结果相同。

4

2 回答 2

14

我刚刚找到了一个更新的文档(我最初链接到的那个似乎很旧),它解释了这种实现行为:

一般来说,异步任务要么在调用操作的线程中执行,例如发送HTTP 请求,要么在客户端的executor提供的线程中执行。相关任务,即由返回的 CompletionStages 或 CompletableFutures 触发的那些,没有明确指定执行程序,在与 的默认执行程序相同的默认执行程序CompletableFuture执行,或者如果操作在注册相关任务之前完成,则在调用线程中执行。

而默认的执行者CompletableFuture是公共池。

我还找到了引入此行为的错误 ID,API 开发人员在其中对其进行了充分解释:

2) 依赖任务运行在公共池中依赖任务的默认执行已更新为与 CompletableFuture 的 defaultExecutor 在同一执行器中运行。这对于已经使用 CF 的开发人员来说更为熟悉,并减少了 HTTP 客户端因线​​程不足而无法执行其任务的可能性。这只是默认行为,如果需要,HTTP 客户端和 CompletableFuture 都允许更细粒度的控制。

于 2018-08-23T09:51:00.343 回答
10

简短版:我认为您已经确定了一个实现细节,并且“在实用的地方”意味着不保证提供的executor将被使用。

详细地:

我从这里下载了 JDK 11 源代码。(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 {

此类使用executorifisInSelectorThread为 true,否则任务内联执行。这归结为:

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

于 2018-08-20T13:33:09.487 回答