1

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

}
Zabuzard
  • 25,064
  • 8
  • 58
  • 82
Adrian Smith
  • 1,013
  • 1
  • 13
  • 21

1 Answers1

3

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.

Zabuzard
  • 25,064
  • 8
  • 58
  • 82
  • Is there a way to trigger an event when the headers and body have been received. I was hoping to be able to get the URL data and interrupt the main thread when the data was ready. Similar to the XMLHttpRequest Object in JavaScript. Great answer and many thanks. – Adrian Smith Sep 22 '20 at 08:13
  • 2
    Sure, you can `compose` tasks on top of the future. You are already doing it with `thenApply` and `thenAccept`. You can compose as many things on top of the future as you like. – Zabuzard Sep 22 '20 at 09:20
  • 2
    If you want to do something when the response headers are received you can provide your own `BodyHandler`, which might only be a thin wrapper around a built-in `BodyHandler`. Just make sure not to do any blocking operations in the `BodyHandler` callbacks though, as that may block the `HttpClient` thread and could wedge the whole response handling. – daniel Sep 22 '20 at 14:35