Java Concurrency Interview

CompletableFuture and async composition question 95 of 180 mid

Why should you pass an explicit executor to the *Async methods?

supplyAsync(task) and thenApplyAsync(fn) default to ForkJoinPool.commonPool(). It has availableProcessors() - 1 threads and is shared with every parallel stream in the JVM, including those inside libraries you did not write. Block on I/O there and you have taken a scarce global resource out of circulation.

There is a further trap: on a single-core machine, or in a container that reports one CPU, the common pool has a parallelism of 1, and CompletableFuture refuses a pool that small: every *Async call without an executor starts a brand-new thread instead. Nothing bounds that, so a burst of calls becomes a burst of threads.

Pass an executor sized for the work: a bounded pool for blocking calls, and ideally a separate one per downstream dependency so a slow service cannot take the others with it.

completablefuture executors

Quiz yourself on CompletableFuture and async composition Suggest a correction