38. Structured Concurrency
Structured concurrency (StructuredTaskScope, Java 21+) treats a group of related concurrent tasks as a single unit of work, so failures and cancellations propagate cleanly and threads don't leak.
Explanation
Structured concurrency (JEP 453) ensures that the lifetime of concurrent tasks is scoped to a structured block. When the block exits, all subtasks are guaranteed to have completed (or been cancelled). This eliminates the common bugs of unstructured concurrency: tasks that outlive their context, leaked threads, and partial failures that leave state inconsistent.
StructuredTaskScope.ShutdownOnFailure cancels all remaining subtasks as soon as any one fails, then re-throws the exception in the parent. StructuredTaskScope.ShutdownOnSuccess cancels remaining tasks as soon as one succeeds (useful for racing alternatives).
The pattern: open a scope, fork subtasks, join (wait for completion policy), check results/exceptions, use results. All inside a try-with-resources block so the scope is always closed.
Example
import java.util.concurrent.StructuredTaskScope;
import java.util.concurrent.StructuredTaskScope.Subtask;
public class StructuredConcurrencyDemo {
record UserProfile(String name, String email) {}
record UserOrders(int count) {}
static UserProfile fetchProfile(long userId) throws InterruptedException {
Thread.sleep(50); // simulate DB call
return new UserProfile("Alice", "alice@example.com");
}
static UserOrders fetchOrders(long userId) throws InterruptedException {
Thread.sleep(80); // simulate DB call
return new UserOrders(42);
}
static void handleRequest(long userId) throws Exception {
// ShutdownOnFailure: if either subtask fails, cancel the other
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
Subtask<UserProfile> profileTask = scope.fork(() -> fetchProfile(userId));
Subtask<UserOrders> ordersTask = scope.fork(() -> fetchOrders(userId));
scope.join() // wait for both to finish
.throwIfFailed(); // re-throw if any failed
// Both succeeded โ safe to call .get()
UserProfile profile = profileTask.get();
UserOrders orders = ordersTask.get();
System.out.printf("User: %s, Orders: %d%n", profile.name(), orders.count());
}
}
public static void main(String[] args) throws Exception {
handleRequest(1L);
}
}Sources
- https://openjdk.org/jeps/453 โ JEP 453: Structured Concurrency (preview Java 21)
- https://dev.java/learn/new-features/structured-concurrency/ โ Structured concurrency guide