Java 11 HTTP 客户机异步执行

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

在可行的情况下,返回实例的异步任务和依赖操作在客户端提供的线程上执行。CompletableFutureExecutor

据我所知,这意味着如果我在创建对象时设置了自定义执行器: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
});

但是,在上面的依赖操作(中的使用者)中,我看到执行此操作的线程来自公共池而不是自定义执行程序,因为它打印 。thenAcceptThread is: ForkJoinPool.commonPool-worker-5

这是实现中的错误吗?还是我错过了什么?我注意到它说“实例在客户端的执行器提供的线程上执行,如果可行”,那么这是不应用这种情况吗?

请注意,我也尝试过,结果是相同的。thenAcceptAsync


答案 1

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

通常,异步任务在调用操作的线程中执行,例如发送HTTP请求,或者由客户端的执行器提供的线程执行。依赖任务,即由返回的 CompleteStages 或 CompletableFuture 触发的那些未显式指定执行程序的任务,在与 CompletableFuture 相同的默认执行程序中执行,或者如果操作在从属任务注册之前完成,则在调用线程中执行。

的默认执行器是公共池。CompletableFuture

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

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


答案 2

简短版本:我认为您已经确定了实现细节,并且“在可行的情况下”意味着无法保证所提供的内容将被使用。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 {

此类使用 if 为 true,否则任务将以内联方式执行。这可以归结为:executorisInSelectorThread

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

其中 是 .编辑:这个类也包含在:selmgrSelectorManagerHttpClientImpl.java

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

结果是:我猜想实际情况意味着它依赖于实现,并且不能保证所提供的将被使用。executor

注意:这与默认执行程序不同,在默认执行程序中,构建器不提供 .在这种情况下,代码清楚地创建了一个新的缓存线程池。换句话说,如果构建器提供了 一个 ,则进行身份检查。executorexecutorSelectorManager


推荐