JEP 428, Structured Concurrency (Incubator), has been promoted from Proposed to Target to Targeted status for JDK 19. Under the umbrella of Project Loom, this JEP proposes simplifying multithreaded programming by introducing a library to treat multiple tasks running on different threads as an atomic operation. As a result, it will streamline error handling and cancellation, improve reliability, and enhance observability. This is still an incubating API.
This allows developers to organize their concurrency code using the StructuredTaskScope
class. It will treat a family of subtasks as a unit. The subtasks will be created on their own threads by forking them individually but then joined as a unit and possibly canceled as a unit; their exceptions or successful results will be aggregated and handled by the parent task. Let’s see an example:
Response handle() throws ExecutionException, InterruptedException
try (var scope = new StructuredTaskScope.ShutdownOnFailure())
Future<String> user = scope.fork(() -> findUser());
Future<Integer> order = scope.fork(() -> fetchOrder());
scope.join(); // Join both forks
scope.throwIfFailed(); // ... and propagate errors
// Here, both forks have succeeded, so compose their results
return new Response(user.resultNow(), order.resultNow());
The above handle()
method represents a task in a server application. It handles an incoming request by creating two subtasks. Like ExecutorService.submit()
, StructuredTaskScope.fork()
takes a Callable
and returns a Future
. Unlike ExecutorService
the returned Future
is not joined via Future.get()
. This API runs on top of JEP 425, Virtual Threads (Preview), also targeted for JDK 19.
The examples above use the StructuredTaskScope
API, so to run them on JDK 19, a developer must add the jdk.incubator.concurrent
module, as well as enable preview features to use virtual threads:
Compile the above code as shown in the following command:
javac --release 19 --enable-preview --add-modules jdk.incubator.concurrent Main.java
The same flag is also required to run the program:
java --enable-preview --add-modules jdk.incubator.concurrent Main
;
However, one can directly run this using the source code launcher. In that case, the command line would be:
java --source 19 --enable-preview --add-modules jdk.incubator.concurrent Main.java
The jshell option is also available, but requires enabling the preview feature as well:
jshell --enable-preview --add-modules jdk.incubator.concurrent
The benefits structured concurrency brings are numerous. It creates a child-parent relationship between the invoker method and its subtasks. For instance, from the example above, the handle()
task is a parent and its subtasks, findUser()
and fetchOrder()
, are children. As a result, the whole block of code becomes atomic. It ensures observability by demonstrating the task hierarchy in the thread dump. It also enables short-circuiting in error handling. If one of the sub-tasks fails, the other tasks will be canceled if not completed. If the parent task’s thread is interrupted before or during the call to join()
, both forks will be automatically canceled when the scope exits. These bring clarity to the structure of the concurrent code, and the developer can now reason and follow the code as if they read through as if they are running in a single-threaded environment.
In the early