Lesson 26: Structured Concurrency (StructuredTaskScope) – Java 21+

Java 21 introduces Structured Concurrency to simplify and organize concurrent programming. It’s part of Project Loom, designed to make managing multiple threads (especially Virtual Threads) more readable, reliable, and easier to cancel or handle as a group.


1. What Is Structured Concurrency?

✅ Structured concurrency treats multiple concurrent tasks as a single unit of work.
✅ It ensures that tasks start, run, and finish together within a scope.
✅ If one task fails, the entire group can be cancelled or handled cleanly.

🧠 Think of it like:

If one thread in a group fails, cancel the rest, and wait for them all to finish together.


2. Before Structured Concurrency: Manual Thread Management

🔴 Manual thread handling required:

  • Creating a thread pool
  • Tracking each task
  • Manually shutting them down
  • Handling exceptions independently

📌 Example (Before Java 21 – Manual & Messy)

ExecutorService executor = Executors.newFixedThreadPool(2);
Future<String> result1 = executor.submit(() -> fetchData1());
Future<String> result2 = executor.submit(() -> fetchData2());

String data1 = result1.get(); // Might block or throw
String data2 = result2.get();

executor.shutdown();

No built-in way to cancel both if one fails.


3. Java 21+ Solution: StructuredTaskScope

✅ Java 21 introduces StructuredTaskScope, a powerful way to:

  • Run tasks in parallel
  • Wait for all or any to complete
  • Handle errors and timeouts as a group

4. Example: Run Two Tasks in Parallel and Wait for Both

📌 Using StructuredTaskScope.ShutdownOnFailure

import java.util.concurrent.StructuredTaskScope;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
            
            StructuredTaskScope.Subtask<String> userTask = scope.fork(() -> fetchUser());
            StructuredTaskScope.Subtask<String> ordersTask = scope.fork(() -> fetchOrders());

            scope.join();            // Wait for both
            scope.throwIfFailed();   // Propagate exceptions if any

            String user = userTask.get();
            String orders = ordersTask.get();

            System.out.println("User: " + user);
            System.out.println("Orders: " + orders);
        }
    }

    static String fetchUser() throws InterruptedException {
        Thread.sleep(500); // Simulate delay
        return "Alice";
    }

    static String fetchOrders() throws InterruptedException {
        Thread.sleep(800); // Simulate delay
        return "Order#12345";
    }
}

Output:

User: Alice
Orders: Order#12345

✔ If either fetchUser() or fetchOrders() failed, the other would be automatically cancelled.


5. Variants of StructuredTaskScope

ClassBehavior
StructuredTaskScope.ShutdownOnFailure✅ Cancel all subtasks if any fails
StructuredTaskScope.ShutdownOnSuccess✅ Stop all subtasks when one successfully completes
StructuredTaskScopeManual control (neutral behavior)

6. Real-World Scenarios

🚀 Use Structured Concurrency when:

  • You need to fetch data from multiple APIs simultaneously.
  • You’re running parallel background jobs, but one failure should cancel the group.
  • You want simple cancellation and error handling for Virtual Threads.

✅ Example: Use ShutdownOnSuccess for First-Finished Strategy

try (var scope = new StructuredTaskScope.ShutdownOnSuccess<String>()) {
    scope.fork(() -> searchDatabase());
    scope.fork(() -> searchCache());

    scope.join();
    scope.throwIfFailed();

    String result = scope.result(); // First successful result
    System.out.println("Search Result: " + result);
}

7. Comparison: Before vs. After Structured Concurrency

FeatureBefore Java 21Java 21+ (Structured Concurrency)
Manual thread lifecycle✅ Required❌ Handled automatically
Task coordination❌ Error-prone✅ Built-in join() & throwIfFailed()
Cancellation❌ Manual✅ Automatic with ShutdownOnFailure
Virtual Thread support❌ Complex✅ Seamless integration

Lesson Reflection

  1. How does StructuredTaskScope simplify managing multiple threads?
  2. When would ShutdownOnSuccess be more useful than ShutdownOnFailure?
  3. Can you think of a real-world situation where structured concurrency would make code safer or easier to debug?

The next Java enhancement: Foreign Function & Memory API (FFM API) introduced in Java 22 😊🚀

Java Sleep