Step 1 of 7 · Reading · ~3 min
Learn
Concurrency
Executors and CompletableFuture
Raw Thread objects make you create, start and join every worker by hand. An executor separates the work (a task) from the machinery that runs it, and CompletableFuture lets you describe what happens with the result before it exists. You will be able to run tasks on a pool, chain stages, and recover from a failure in the middle of a chain.
Pools, tasks and futures
An ExecutorService owns a fixed set of worker threads and a queue. Submitting more tasks than threads is normal: the extras wait in the queue.
ExecutorService pool = Executors.newFixedThreadPool(3);
Callable<Integer> job = () -> Integer.parseInt("42") * 2; // Callable returns a value, may throw
Future<Integer> f = pool.submit(job);
int v = f.get(); // blocks until done: 84
pool.shutdown(); // stop accepting work; queued tasks still run
Runnable returns nothing and cannot throw a checked exception; Callable<V> does both. If a task throws, get() rethrows it wrapped in an ExecutionException; the original is e.getCause(). To run many tasks and wait for all, pool.invokeAll(listOfCallables) returns the futures in the order you gave the tasks, not the order they finished.
Pool threads are not daemon threads. If you forget shutdown(), the JVM keeps running after main returns.
Chaining stages
A CompletableFuture<T> is a future you can attach steps to. Each step returns a new future, so a chain reads top to bottom:
CompletableFuture<String> label =
CompletableFuture.supplyAsync(() -> Long.parseLong("9000000000"), pool) // stage 1, on the pool
.thenApply(big -> big / 1000) // map the value
.thenApply(k -> k + " thousand");
| Method | Use it when |
|---|---|
thenApply(fn) | fn returns a plain value (map) |
thenCompose(fn) | fn itself returns a CompletableFuture (flat-map) |
thenCombine(other, fn) | you need the results of two independent futures |
CompletableFuture.allOf(a, b, c) | you only need to know when all of them are done |
Pass the pool as the last argument. Without it, supplyAsync uses the shared ForkJoinPool.commonPool(), which is sized for CPU work and is also used by parallel streams.
Failure travels down the chain
If any stage throws, every later thenApply is skipped and the failure arrives at the first stage that handles it. Two details catch everyone:
CompletableFuture<Integer> g =
CompletableFuture.supplyAsync(() -> Long.parseLong("12x"), pool) // throws NumberFormatException
.thenApply(n -> n * 2) // skipped
.handle((n, err) -> err == null ? 1 : -1);
The err that handle receives is a CompletionException whose getCause() is the NumberFormatException; the original is wrapped once. Printing err.getMessage() gives java.lang.NumberFormatException: ... and err.getClass() gives the wrapper, which is rarely what you want. handle runs for success and failure (one of the two arguments is null); exceptionally runs only on failure; whenComplete observes without changing the result.
Second: allOf(...).join() throws if any input failed. Recover each future first (with handle) and then wait on the recovered ones, or the one bad item takes down the whole batch.
join() throws the unchecked CompletionException; get() throws the checked ExecutionException. They carry the same cause.
Common mistakes
- Reading
err.getMessage()orerr.getClass()directly inhandle. You printCompletionExceptionor the cause's class name with a package prefix instead of the real exception. - Calling
get()inside the loop that submits. Each call blocks, so the pool runs one task at a time. Submit everything, then collect. - Forgetting
shutdown(). The program prints its result and then never exits. thenApplywhere the function returns a future. You getCompletableFuture<CompletableFuture<T>>and the inner failure is never seen.
Your exercise
Complete run, which turns one line of input into a future of one output line, handling both a bad number and a division by zero. The reading, the pool and the printing are in the starter. The likely mistake is reporting the wrapper exception's name instead of the real one.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…