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

Java Async HttpClient request seems to block the main thread?

根据 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

Returns the result value when complete, or throws an (unchecked) exception if completed exceptionally. [...]

虽然没有明确提到但是从名字上就可以看出(参考Thread#join其中“等待这个线程死亡”),它只能return等待调用完成的结果。

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

Waits if necessary for this future to complete, and then returns its result.


解决方案

将未来放入一个变量中,稍后加入,当你真正想要等待它时。

例如:

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

或者从不等待它。那么你的 main-thread 可能会更早死掉,但 JVM 只会在所有 non-daemon 线程都死掉并且 HttpClient 用于异步任务的线程不是守护线程后才会关闭。


备注

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

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

请记住,OS 调度程序可以自由决定它执行什么的顺序 - 它可以是任何顺序。