The 30 second answer
Structured concurrency treats a group of subtasks as one unit of work. I open a scope, fork subtasks like fetching a user and their orders, then join. If one fails, the others are cancelled, and no subtask can outlive the scope, so no thread leaks. In Java 21 it is only a preview API, so it needs the enable-preview flag and isn't final yet.
In Java 21 the API is StructuredTaskScope (JEP 453, preview; compile and run with --enable-preview):
Response handle() throws Exception {
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
Subtask<User> user = scope.fork(() -> findUser());
Subtask<Order> order = scope.fork(() -> fetchOrder());
scope.join().throwIfFailed(); // wait, rethrow the first failure
return new Response(user.get(), order.get());
}
}ShutdownOnFailurecancels the remaining subtasks when one fails.ShutdownOnSuccessreturns the first successful result and cancels the rest.- Subtasks run in new virtual threads by default, and thread dumps show the parent-child relationship.
Compared with ExecutorService plus Future, you can't forget to cancel siblings or leak a task after the method returns.
Status after 21: still a preview API in later releases. Java 25 (JEP 505) redesigned it: scopes are opened with StructuredTaskScope.open() and policies are given as joiners. Check the JEP for the JDK you use.
Sources: JEP 453: Structured Concurrency (Preview), JEP 505: Structured Concurrency (Fifth Preview)