JEP 453「構造化同時実行(プレビュー)」は、JDK 21のTargetedステータスから統合された。以前はインキュベートAPIであったが、この最初のプレビューでは、過去2回のインキュベートからのフィードバックに対応した機能強化が盛り込まれている。JEP 428「構造化同時実行(インキュベーター」(JDK 19で提供)、JEP 437「構造化同時実行(セカンドインキュベーター)」(JDK 20で提供)。現在の提案における唯一の重要な変更は、StructuredTaskScope::fork(...)
メソッドがFuture
ではなくSubtask
を返すということである。これはプレビュー機能である。
JDK 21のStructured Concurrencyは、構造化された並行処理のためのAPIを導入することで、並行プログラミングを簡素化することを目的としている。このアプローチでは、異なるスレッドで実行される関連タスクのグループを1つの作業単位として扱うため、エラー処理とキャンセルが効率化され、信頼性が向上し、観測可能性が強化される。例を見てみよう。
Response handle() throws ExecutionException, InterruptedException {
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
Supplier<String> user = scope.fork(() -> findUser());
Supplier<Integer> order = scope.fork(() -> fetchOrder());
scope.join() // Join both subtasks
.throwIfFailed(); // ... and propagate errors
// Here, both subtasks have succeeded, so compose their results
return new Response(user.get(), order.get());
}
//...
}
このコードでは、新しいStructuredTaskScope
を作成し、それを使用して、findUser()
を実行するサブタスクとfetchOrder()
を実行するサブタスクの2つをフォークする。両方のサブタスクが完了すると、両方のサブタスクの結果を使用して新しいResponseを
作成する。
構造化同時実行はプレビューAPIであり、デフォルトでは無効になっている。StructuredTaskScope
APIを使用するには、次のコマンドに示すように、開発者がプレビューAPIを有効にしてこのコードをコンパイルする必要がある。
javac --release 21 --enable-preview Main.java
また、プログラムを実行する際にも、同じフラグが必要である。
java --enable-preview Main
しかし、ソースコードランチャーを使って、これを直接実行できる。その場合、コマンドラインは次のようになる。
java --source 21 --enable-preview Main.java
また、jshellオプションも利用できるが、プレビュー機能を有効にする必要がある。
jshell --enable-preview
実際には、StructuredTaskScope
のほとんどの用途は、StructuredTaskScope
クラスを直接利用せず、シャットダウン・ポリシーを実装する2つのサブクラスのいずれかを利用する。これらのサブクラス、ShutdownOnFailure
と ShutdownOnSuccess
は、それぞれ最初のサブタスクが失敗または成功したときにスコープをシャットダウンするパターンをサポートする。
構造化並行処理では、異なるスレッドで実行される関連タスクのグループを、1つの作業単位として扱う。このアプローチにより、エラー処理とキャンセルが効率化され、信頼性が向上し、観測可能性が高まった。開発者のRon Pressler氏(Oracleの技術スタッフでOpenJDKのProject Loomの技術リーダー)とAlan Bateman氏(OracleのJava Platform Groupのエンジニア)は、スレッドリークやキャンセル遅延といった並行プログラミングに伴う一般的なリスクを排除し、並行コードの観察可能性を高めることを意図している。
この新機能は、ExecutorService
やFuture
といったjava.util.concurrent
パッケージの並行処理構造を置き換えることを目的としたものではない。また、Java Platformのための決定的な構造化並行処理APIや、スレッド間でデータストリームを共有するための方法を定義することも目的としていない。
ExecutorService
APIなどの現在の並行プログラミングモデルは、並行性のパターンが無制限であるため、複雑さとリスクをもたらす。これらのモデルは、タスクとサブタスクの関係を強制したり追跡したりしないため、並行タスクの管理と観測が困難である。
構造化並行処理モデルは、タスクの構造がコードの構造を反映することを提案している。シングルスレッド・コードでは、実行は常にタスクとサブタスクの階層を強制し、他に対する各サブタスクの寿命は、コードの構文ブロック構造によって支配される。
新しいStructuredTaskScope
は、ExecutorService
に代わる、よりシンプルで安全な代替手段を提供する。このAPIは、一緒に完了すべき関連タスクのグループをカプセル化し、いずれかのサブタスクが失敗すると残りのサブタスクのキャンセルにつながる。
コード例やこの機能の背景にある動機に関する包括的な議論を含む、この変更の詳細は、OpenJDKのウェブサイトで入手可能である。
この新しいAPIは、並行プログラミングをより簡単に、より信頼性の高い、より観察可能なものにするための重要な一歩である。特に、保守性が高く、信頼性が高く、観測可能なサーバーアプリケーションを構築する上で有益であると期待される。構造化並行処理を深く掘り下げ、裏話を学びたい開発者は、InfoQ Podcast、Ron Pressler氏によるYouTubeセッション、Inside Javaの記事を聞くことができる。