Java 11 HTTP客户端异步执行
我正在尝试来自JDK 11的新HTTP客户端API,特别是它执行请求的异步方式。但有一点我不确定自己是否理解(某种程度上是实现方面)。在报告中,它说: 返回的Java 11 HTTP客户端异步执行,java,http,asynchronous,completable-future,java-11,Java,Http,Asynchronous,Completable Future,Java 11,我正在尝试来自JDK 11的新HTTP客户端API,特别是它执行请求的异步方式。但有一点我不确定自己是否理解(某种程度上是实现方面)。在报告中,它说: 返回的CompletableFuture实例的异步任务和依赖操作在客户机的执行器提供的线程上执行(如果可行) 据我所知,这意味着如果我在创建HttpClient对象时设置了自定义执行器: ExecutorService executor = Executors.newFixedThreadPool(3); HttpClient httpClie
CompletableFuture
实例的异步任务和依赖操作在客户机的执行器提供的线程上执行(如果可行)
据我所知,这意味着如果我在创建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
});
但是,在上面的依赖操作中(consumer在然后accept
),我看到执行它的线程来自公共池,而不是自定义执行器,因为它打印的线程是:ForkJoinPool.commonPool-worker-5
这是实现中的一个bug吗?还是我错过了什么?我注意到它说“实例是在客户机的执行器提供的线程上执行的,,如果可行的话,”,那么这是不适用的情况吗
请注意,我也尝试了验收同步,结果也是一样的。简短版本:我认为您已经确定了一个实现细节,“在实际情况下”意味着无法保证将使用提供的执行器
详细内容:
我已经从下载了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 {
如果IsInSelectThread
为true,则此类使用executor
,否则任务将以内联方式执行。这归结为:
boolean isSelectorThread() {
return Thread.currentThread() == selmgr;
}
其中,selmgr
是一个选择器管理器
Edit:此类也包含在HttpClientImpl.java
中:
// Main loop for this client's selector
private final static class SelectorManager extends Thread {
结果:我猜测,在实际情况下,这意味着它依赖于实现,并且不能保证所提供的执行器将被使用
注意:这与默认执行器不同,在默认执行器中,生成器不提供执行器。在这种情况下,代码显然会创建一个新的缓存线程池。换句话说,如果构建器提供了一个执行器
,则会对SelectorManager
进行身份检查。我刚刚找到一个更新的(我最初链接到的那一个看起来很旧),它解释了这个实现行为:
通常,异步任务要么在调用操作的线程(例如HTTP请求)中执行,要么由客户端服务器提供的线程执行相关任务,由返回的CompletionStages或CompletableFutures触发的任务,这些任务没有明确指定执行者,执行方式与CompletableFuture
相同,如果操作在注册相关任务之前完成,则执行方式与调用线程相同
CompletableFuture
的默认执行器是公共池
我还发现了引入这种行为的方法,API开发人员在其中充分解释了这一点:
2) 依赖任务在公共池中运行
依赖任务的默认执行已更新为与CompletableFuture的默认执行器在同一执行器中运行。这对于已经使用CF的开发人员来说更为熟悉,并且降低了HTTP客户机执行其任务时线程不足的可能性。这只是默认行为,如果需要,HTTP客户端和CompletableFuture都允许更精细的控制
抱歉,如果这很愚蠢,请帮助我理解,您是如何解释它来自公共池而不是自定义执行器的,因为它打印的线程是:ForkJoinPool.commonPool-worker-5?…我还尝试了System.out.println(httpClient.executor().get().equals(executor))
在然后accept
消费者中,它打印true
@nullpointer,我假设他打印出线程.currentThread().getName()
在中,然后接受使用者,该名称表示线程
来自公共ForkJoinPool
而非自定义执行器
。换句话说,OP并不是说HttpClient
的Executor
已经更改,OP想知道为什么依赖CompletableFuture
阶段使用不同的线程池执行。@nullpointer正是Slaw所说的。我还知道线程来自公共池,因为我可以为自定义执行器创建的线程指定特殊名称,以清楚地标识它们。对于httpClient.executor()
,此方法只返回我在创建时指定的executor,这不是thenAccept
所使用的。@Slaw@manouti谢谢。我得到了你们两位所指的,确实尝试了向执行器提供一个自定义命名线程,并且可以看到它没有在然后accept
中使用。将进一步查找有关其实际部分以及bug数据库的详细信息。结果表明,在这个API的开发过程中,文档已经更新,因此它描述了这种行为。最新的文档链接感谢您的回答。这似乎确实是一个实施细节。但是,关于最后一点,当我没有在客户端设置中指定执行器时,依赖操作仍然使用公共池,而不是幕后的默认线程缓存执行器。“这只是默认行为,HTTP客户端和CompletableFuture都允许更精细的控制,如果需要的话。”对于CompletableFuture
,我假设他的意思是使用*Async(…,Executor)
方法变量。然而