Dustin's Dev Notes

Java Concurrency Patterns for High-Throughput Services

With Java 21's Project Loom going GA, the concurrency landscape has fundamentally changed. Here are my benchmark results and patterns for production services handling 10K+ concurrent connections.

Virtual Threads vs Thread Pools

The biggest surprise from our benchmarks: virtual threads consistently outperformed traditional thread pools for I/O-bound workloads by 3-5x. For a typical REST API service:

// Traditional approach
ExecutorService pool = Executors.newFixedThreadPool(200);

// Virtual threads (Java 21+)
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    executor.submit(() -> handleRequest(request));
}

Structured Concurrency

Java 21's structured concurrency (preview) makes managing subtasks much cleaner:

try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
    Subtask<User> user = scope.fork(() -> fetchUser(id));
    Subtask<Order> orders = scope.fork(() -> fetchOrders(id));
    scope.join().throwIfFailed();
    return new Response(user.get(), orders.get());
}

Benchmark Results

ApproachThroughput (req/s)P99 LatencyMemory
FixedThreadPool(200)12,40089ms512MB
Virtual Threads41,20034ms380MB