Maximizing Concurrency while making RESTful calls from a client in Java

Maximizing Concurrency while making Restful calls from a client in Java

Scale your HTTP requests without blocking threads by leveraging Java 11’s built-in async client. Here are five best practices:

  1. Configure a Reusable HttpClient • Enable HTTP/2 for multiplexing • Set a global connect timeout

  2. Build Immutable HttpRequests • Define URI, method, headers, and per-request timeouts • Keep requests thread-safe and reusable

  3. Use `sendAsync` for Non-Blocking Calls • Returns a `CompletableFuture<HttpResponse<T>>` • Avoids blocking the calling thread

  4. Aggregate with `CompletableFuture.allOf` • Dispatch dozens or hundreds of requests in parallel • Wait for all to complete before processing results

  5. Handle Errors & Timeouts Gracefully • Use `.exceptionally()` or `.handle()` to catch failures • Apply `.orTimeout()` for fine-grained control

See https://github.com/explorer436/programming-playground/tree/main/java-playground/my-implementations/java-multithreading-and-concurrency-demo

Solid starting point, but a few gaps:

  • No mention of connection pooling. Without it, repeated TCP handshakes will throttle throughput, especially under high concurrency.
  • .join() is blocking. Using it on the main thread negates async benefits. Prefer callback chaining (thenCompose, thenAccept) for true non-blocking behavior. .join() is a blocking operation and it will make main thread to wait untill all operations are not over. Using join() at the end will not make it truly async.
  • HTTP/2 helps, but without tuning the underlying executor or limiting concurrent inflight requests, you’ll hit scaling ceilings fast.
  • This won’t scale as we might believe. We need an http connection pool because tcp handshakes will become the bottleneck for throughput.

Links to this note