Java异步HttpClient请求似乎阻塞了主线程?

问题描述

根据this,以下代码段应为“异步”。

因此,输出应为: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");

}

解决方法

说明

您致电join(),它将明确等待并阻止,直到将来完成。

来自CompletableFuture#join

完成后返回结果值,如果异常完成则抛出(未经检查的)异常。 [...]

尽管没有明确提及,但从名称上可以明显看出(请参阅Thread#join,其中“等待该线程死亡。” ),它只能通过等待调用来返回结果。完成。

该方法与CompletableFuture#get非常相似,它们在异常完成方面的行为有所不同:

等待将来完成,然后返回结果。


解决方案

将future放入变量中,然后在您真正想要等待时加入。

例如:

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

或者永远不要等待它。然后,您的主线程可能会更早死亡,但是只有在所有非守护程序线程都死了并且HttpClient用于异步任务的线程不是守护程序线程之后,JVM才会关闭。


注意

此外,永远不要依赖多线程执行的顺序。

即使您不会犯错,您观察到的顺序也将是多线程执行的有效顺序

请记住,操作系统调度程序可以自由决定执行命令的顺序-可以是任何顺序。