Java 11 HTTP客户端异步执行

man*_*uti 13 java asynchronous http completable-future java-11

我正在尝试JDK 11中的新HTTP客户端API,特别是它执行请求的异步方式.但有些事情我不确定我理解(有点像实现方面).在文档中,它说:

在可行的CompletableFuture情况下,返回实例的异步任务和相关操作将在客户端提供的线程上执行Executor.

据我所知,这意味着如果我在创建HttpClient对象时设置自定义执行程序:

ExecutorService executor = Executors.newFixedThreadPool(3);

HttpClient httpClient = HttpClient.newBuilder()
                      .executor(executor)  // custom executor
                      .build();
Run Code Online (Sandbox Code Playgroud)

然后,如果我异步发送请求并在返回时添加依赖操作CompletableFuture,则依赖操作应在指定的执行程序上执行.

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

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

这是实施中的错误吗?或者我错过了什么?我注意到它说"实例是在客户端的执行程序提供的线程上执行的,在实际可行的情况下",这是不适用的情况?

请注意,我也尝试了thenAcceptAsync,结果相同.

man*_*uti 10

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

通常,异步任务在调用操作的线程中执行,例如发送 HTTP请求,或者由客户端执行程序提供的线程执行.相关的任务,那些被退回CompletionStages或CompletableFutures触发,没有明确指定遗嘱执行人,执行在同一个默认的执行为的CompletableFuture,或者调用线程,如果从属任务被注册前的操作完成.

默认的执行者CompletableFuture是公共池.

我还发现了引入此行为的错误ID,其中API开发人员完全解释了这一点:

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


Mic*_*ter 8

简短版本:我认为您已经确定了一个实现细节," 实际可行 "意味着无法保证所提供的内容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 {
Run Code Online (Sandbox Code Playgroud)

此类使用executorif isInSelectorThread为true,否则任务以内联方式执行.这归结为:

boolean isSelectorThread() {
    return Thread.currentThread() == selmgr;
}
Run Code Online (Sandbox Code Playgroud)

这里selmgr是一个SelectorManager.编辑:此类还包含在HttpClientImpl.java:

// Main loop for this client's selector
private final static class SelectorManager extends Thread {
Run Code Online (Sandbox Code Playgroud)

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

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