Skip to content

Week 14 - Executors, CompletableFuture, and the Pre-Loom World

Conceptual Core

Before virtual threads, the standard answer to "how do I run a lot of concurrent work without 100,000 OS threads" was asynchronous programming: CompletableFuture, reactive streams (Reactor / RxJava), or Netty's event loop. These still exist, still ship, and still appear in every framework. You must read them fluently - even though Loom obsoletes most reasons to write them.

Mechanical Detail

  • ExecutorService and ThreadPoolExecutor - core/max pool size, queue choice (the LinkedBlockingQueue default is unbounded and the cause of half of all Java OOMs).
  • ForkJoinPool - work-stealing, the substrate for parallelStream and (originally) CompletableFuture.async*. The common pool is shared - don't park forever on it.
  • CompletableFuture: supplyAsync, thenApply, thenCompose, thenCombine, exceptionally, handle. The "monadic composition" model. Pitfalls: exceptions wrapped in CompletionException, easy to forget the executor, chains that span threads with no structured cancellation.
  • Reactive Streams + Project Reactor (Mono, Flux): backpressure, schedulers, the operator zoo. Spring WebFlux is built on this. The honest summary in 2026: most teams that adopted WebFlux for scaling reasons should now seriously consider virtual threads + plain blocking code; reactive remains right where you need explicit backpressure or operator fusion over data streams.

Lab

Implement the same web-scraper-with-fan-out three ways: blocking ExecutorService, CompletableFuture chain, Reactor Flux.flatMap. Compare lines of code, readability, and error handling. Keep them; you will re-do the same task with virtual threads next week.

Idiomatic Drill

Find a CompletableFuture chain in any large codebase. Trace which thread executes each stage. (Almost nobody gets this right on first read - that's the point.)

Production Hardening Slice

Audit every ExecutorService in your code: bounded queue? rejection policy? named threads (ThreadFactory)? graceful shutdown (shutdown + awaitTermination + shutdownNow fallback)?

Comments