英文:
Java Async HttpClient request seems to block the main thread?
问题
根据这个链接,以下代码片段应该是异步的。
因此,输出应该为:TP1,TP2,TP3,http://openjdk.java.net/。
然而,当我运行代码时,得到的却是:TP1,TP2,http://openjdk.java.net/,TP3。
看起来 "sendAsync" 方法阻塞了主线程。这不是我从一个异步方法中期望的行为。
我是否做错了什么?
public static void main(String[] args) {
HttpClient client = HttpClient.newHttpClient();
System.out.println("TP1");
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("http://openjdk.java.net/"))
.build();
System.out.println("TP2");
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::uri)
.thenAccept(System.out::println)
.join();
System.out.println("TP3");
}
英文:
According to this the following snippet should be Async.
Therefore, the output should read: TP1, TP2, TP3, http://openjdk.java.net/.
However, when I run it I get: TP1, TP2, http://openjdk.java.net/, TP3.
It seems "sendAsync" is blocking the main thread. This is not what I expected from an Async method.
Am I doing something wrong?
public static void main(String[] args) {
HttpClient client = HttpClient.newHttpClient();
System.out.println("TP1");
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("http://openjdk.java.net/"))
.build();
System.out.println("TP2");
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::uri)
.thenAccept(System.out::println)
.join();
System.out.println("TP3");
}
答案1
得分: 3
Explanation
您调用join()
,这会显式地等待并阻塞,直到未来完成为止。
> 当完成时返回结果值,如果异常完成,则抛出(未经检查的)异常。 [...]
虽然没有明确提到,但从名称中可以明显看出(参见Thread#join,它“等待此线程死亡。”),它只能通过等待调用完成来返回结果。
该方法与CompletableFuture#get非常相似,它们在异常完成方面的行为有所不同:
> 必要时等待此未来完成,然后返回其结果。
解决方案
将未来放入变量中,稍后再加入,以便在您实际希望等待它时再加入。
例如:
System.out.println("TP2");
var task = client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::uri)
.thenAccept(System.out::println);
System.out.println("TP3");
task.join(); // 稍后等待
或者根本不要在其上等待。那么您的主线程可能会更早地终止,但是只有在所有非守护线程都死亡并且HttpClient
用于异步任务的线程不是守护线程时,JVM 才会关闭。
注意
此外,永远不要依赖多线程执行的顺序。
即使您没有犯错,您观察到的顺序也可能是多线程执行的有效顺序。
请记住,操作系统调度程序可以自由决定以哪个顺序执行什么操作 - 它可以是任何顺序。
英文:
Explanation
You call join()
and that will explicitly wait and block until the future is completed.
From CompletableFuture#join:
> Returns the result value when complete, or throws an (unchecked) exception if completed exceptionally. [...]
Although not explicitly mentioned but obvious from the name (refer to Thread#join which "Waits for this thread to die."), it can only return a result by waiting for the call to complete.
The method is very similar to CompletableFuture#get, they differ in their behavior regarding exceptional completion:
> Waits if necessary for this future to complete, and then returns its result.
Solution
Put the future into a variable and join later, when you actually want to wait for it.
For example:
System.out.println("TP2");
var task = client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::uri)
.thenAccept(System.out::println);
System.out.println("TP3");
task.join(); // wait later
Or never wait on it. Then your main-thread might die earlier but the JVM only shuts down once all non-daemon threads are dead and the thread used by HttpClient
for the async task is not a daemon thread.
Note
Also, never rely on the order of multithreaded execution.
Even if you wouldnt have made a mistake, the order you observe would be a valid order of a multithreaded execution.
Remember that the OS scheduler is free to decide in which order it executes what - it can be any order.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论