Search code examples
javamultithreadingasynchronousjava-http-client

Java Async HttpClient request seems to block the main thread?


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");

}

Solution

  • 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.