- 型パラメータ:
T
- スコープで実行されたタスクの結果タイプ
- すべての実装されたインタフェース:
AutoCloseable
StructuredTaskScope
は、タスクが複数の同時サブタスクに分割され、独自のスレッドで実行されるケースと、メイン・タスクを続行する前にサブタスクが完了する必要があるケースをサポートします。 StructuredTaskScope
を使用すると、構造化プログラミングの順次演算と同様に、同時演算の存続期間が「構文ブロック」によって制限されるようにできます。
基本的な使用
StructuredTaskScope
は、そのパブリック・コンストラクタのいずれかを使用して作成されます。 タスクを実行するためにスレッドを開始するfork
メソッド、すべてのスレッドが終了するまで待機するjoin
メソッド、およびタスク範囲を閉じるためのclose
メソッドを定義します。 APIは、 try-with-resources
構成で使用することを目的としています。 blockのコードは、fork
メソッドを使用してスレッドをフォークし、サブタスクを実行し、スレッドがjoin
メソッドで終了するまで待機してから「結果を処理」を実行するということを意図しています。 結果の処理には、例外の処理や再スローが含まれる場合があります。
try (var scope = new StructuredTaskScope<Object>()) {
Future<Integer> future1 = scope.fork(task1);
Future<String> future2 = scope.fork(task2);
scope.join();
... process results/exceptions ...
} // close
join
およびclose
メソッドはowner (タスク・スコープを開いた/作成したスレッド)によってのみ起動でき、所有者がフォーク後にjoin
メソッドを呼び出さなかった場合、close
メソッドはクローズ後に例外をスローします。
StructuredTaskScope
は、タスク・スコープを閉じずに停止するshutdown
メソッドを定義します。 シャットダウンは、サブタスクが結果(または例外)で完了し、他の未完了サブタスクの結果が不要になった場合に役立ちます。 所有者がjoin
メソッドで待機している間にサブタスクがshutdown
を起動すると、join
がウェイ・ク アップされ、すべての未完了スレッドがinterruptedになり、新しいスレッドがタスク・スコープで開始されなくなります。
一般的なケースのポリシーを持つサブクラス
StructuredTaskScope
の2つのサブクラスは、一般的な場合にポリシーを実装するために定義されています:
-
ShutdownOnSuccess
は、最初の結果を取得し、タスク範囲をシャットダウンして未完了のスレッドを中断し、所有者をウェイ・ク アップします。 このクラスは、サブタスクの結果が("任意の起動")を実行し、他の未完了タスクの結果を待機する必要がない場合に意図されています。 最初の結果を取得するメソッド、またはすべてのサブタスクが失敗した場合に例外をスローするメソッドを定義します。 -
ShutdownOnFailure
は、最初の例外を取得し、タスク範囲を停止します。 このクラスは、すべてのサブタスクの結果が("すべて起動")を必要とする場合を対象としています。サブタスクが失敗した場合、他の未完了サブタスクの結果は不要になります。 サブタスクのいずれかが失敗した場合に例外をスローするメソッドを定義する場合。
次に、2つのクラスを使用する2つの例を示します。 どちらの場合も、2つのURLのロケーション"left"および"right"からリソースをフェッチするために、サブタスクのペアがフォークされます。 最初の例では、正常に完了する最初のサブタスクの結果を取得するShutdownOnSuccessオブジェクトを作成し、タスク範囲を停止してもう一方のサブタスクを取り消します。 サブタスクのいずれかが結果で完了するか、両方のサブタスクが失敗するまで、メイン・タスクはjoin
で待機します。 result(Function)
メソッドを起動して取得結果を取得します。 両方のサブタスクが失敗した場合、このメソッドは、原因としていずれかのサブタスクから例外とともにWebApplicationException
をスローします。
try (var scope = new StructuredTaskScope.ShutdownOnSuccess<String>()) {
scope.fork(() -> fetch(left));
scope.fork(() -> fetch(right));
scope.join();
String result = scope.result
(e -> new WebApplicationException(e));
...
}
joinUntil(Instant)
で待機します。 throwIfFailed(Function)
を起動して、いずれかのサブタスクが失敗した場合に例外をスローします。 サブタスクが失敗しない場合、このメソッドはno-opです。 メイン・タスクでは、Future
のresultNow()
メソッドを使用して結果を取得します。
Instant deadline = ...
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
Future<String> future1 = scope.fork(() -> query(left));
Future<String> future2 = scope.fork(() -> query(right));
scope.joinUntil(deadline);
scope.throwIfFailed
(e -> new WebApplicationException(e));
// both subtasks completed successfully
String result = Stream.of(future1, future2)
.map(Future::resultNow
)
.collect(Collectors.joining(", ", "{ ", " }"));
...
}
StructuredTaskScopeの拡張
StructuredTaskScope
を拡張し、handleComplete
をオーバーライドして、ShutdownOnSuccess
およびShutdownOnFailure
によって実装されたポリシー以外のポリシーを実装できます。 メソッドをオーバーライドすると、たとえば、結果で完了したサブタスクの結果を収集し、失敗したサブタスクを無視できます。 サブタスクが失敗した場合に例外が収集される場合があります。 shutdown
メソッドを起動して停止し、一部の条件が発生したときにjoin
がウェイ・ク アップする場合があります。
サブクラスは、通常、join
メソッドの後に実行されるコードに対して使用可能な結果、状態またはその他の結果を作成するメソッドを定義します。 結果を収集し、失敗したサブタスクを無視するサブクラスは、結果のコレクションを返すメソッドを定義できます。 サブタスクが失敗したときに停止するポリシーを実装するサブクラスは、失敗する最初のサブタスクの例外を取得するメソッドを定義できます。
次に、正常に完了したサブタスクの結果を収集するStructuredTaskScope
実装の例を示します。 結果を取得するためにメイン・タスクで使用されるメソッドresults()
を定義します。
class MyScope<T> extends StructuredTaskScope<T> {
private final Queue<T> results = new ConcurrentLinkedQueue<>();
MyScope() {
super(null, Thread.ofVirtual().factory());
}
@Override
protected void handleComplete
(Future<T> future) {
if (future.state() == Future.State.SUCCESS) {
T result = future.resultNow();
results.add(result);
}
}
// Returns a stream of results from the subtasks that completed successfully
public Stream<T> results() {
return results.stream();
}
}
ツリー構造
タスク・スコープは、新しいタスク・スコープを開いたときに親子関係が暗黙的に確立されるツリーを形成します:- 親子関係は、タスク・スコープで開始されたスレッドが独自のタスク・スコープを開くと確立されます。 タスク・スコープ"A"で開始されたスレッドで、タスク・スコープ"B"を開くと、タスク・スコープ"A"がタスク・スコープ"B"の親である親子関係が確立されます。
- 親子関係はネストによって確立されます。 スレッドがタスク・スコープ"B"を開き、タスク・スコープ"C" ("B"をクローズする前に)を開くと、包含タスク・スコープ"B"はネストされたタスク・スコープ"C"の親になります。
ツリー構造は次のものをサポートします:
- スレッド間での「スコープ値」の継承。
- 確認チェック。 メソッド記述の"タスク範囲に含まれるスレッド"というフレーズは、タスク・スコープまたは子孫スコープで開始されたスレッドを意味します。
次の例は、スコープ値の継承を示しています。 スコープ値USERNAME
は、値"duke
"にバインドされます。 StructuredTaskScope
が作成され、 childTask
を実行するスレッドを起動するためにそのfork
メソッドが起動されます。 スレッドは、タスク・スコープの作成時に取得されたスコープ値「バインド」を継承します。 childTask
のコードは、スコープ値の値を使用するため、値"duke
"を読み取ります。
private static final ScopedValue<String> USERNAME = ScopedValue.newInstance();
ScopedValue.where
(USERNAME, "duke", () -> {
try (var scope = new StructuredTaskScope<String>()) {
scope.fork(() -> childTask());
...
}
});
...
String childTask() {
String name = USERNAME.get
(); // "duke"
...
}
StructuredTaskScope
では、現時点ではツリー構造を公開するAPIは定義されません。
特に指定しないかぎり、このクラスのコンストラクタまたはメソッドにnull
引数を渡すと、NullPointerException
がスローされます。
メモリーの一貫性の影響
Callable
タスクの「フォーク」より前、または含まれているスレッドの所有者スレッドでのアクションhappen-beforeそのタスクによって実行されたアクション。次に、happen-beforeタスク結果は、そのFuture
を介して取得されます。または、タスク・スコープの「参加」の後にスレッドで実行されたアクションをhappen-beforeします。
- Java言語仕様を参照してください:
-
17.4.5 Happens-beforeオーダー
- 導入されたバージョン:
- 19
-
ネストされたクラスのサマリー
修飾子と型クラス説明static final class
異常終了する最初のサブタスクの例外を取得するStructuredTaskScope
。static final class
正常に完了する最初のサブタスクの結果を取得するStructuredTaskScope
。 -
コンストラクタのサマリー
コンストラクタ説明仮想スレッドを作成する名前のない構造化タスク・スコープを作成します。StructuredTaskScope
(String name, ThreadFactory factory) 指定された名前とスレッド・ファクトリを使用して構造化タスク・スコープを作成します。 -
メソッドのサマリー
-
コンストラクタの詳細
-
StructuredTaskScope
public StructuredTaskScope(String name, ThreadFactory factory) 指定された名前とスレッド・ファクトリを使用して構造化タスク・スコープを作成します。 タスク範囲には、モニタリングおよび管理の目的で任意の名前が付けられます。 スレッド・ファクトリは、タスクが「フォーク」の場合にスレッドをcreate
するために使用されます。 タスク・スコープは現在のスレッドによって所有されます。このメソッドは、現在のスレッドの「スコープ値」バインディングを取得して、タスク・スコープで作成されたスレッドによる継承を取得します。 クラス説明の「ツリー構造」セクションには、スコープ値バインディングの継承を目的として親子関係が暗黙的に確立される方法の詳細が示されています。
- パラメータ:
name
- タスク範囲の名前。NULLにできますfactory
- スレッドのファクトリ
-
StructuredTaskScope
public StructuredTaskScope()仮想スレッドを作成する名前のない構造化タスク・スコープを作成します。 タスク・スコープは現在のスレッドによって所有されます。このコンストラクタは、
null
という名前の2引数コンストラクタと、仮想スレッドを作成するスレッド・ファクトリの起動と同等です。- 例外:
UnsupportedOperationException
- プレビュー機能が有効になっていない場合
-
-
メソッドの詳細
-
handleComplete
スコープが停止する前にタスクが完了したときに起動されます。handleComplete
メソッドはスレッド・セーフである必要があります。 複数のスレッドによって同時に起動できます。- 実装要件:
- デフォルト実装は何も実行しません。
- パラメータ:
future
- 完了済タスク
-
fork
指定されたタスクを実行する新しいスレッドを開始します。新しいスレッドは、タスク・スコープ
ThreadFactory
を使用して作成されます。 現在のスレッドの「スコープ値」バインディングを継承します。 バインディングは、タスク・スコープの作成時に取得されたバインディングと一致する必要があります。タスク・スコープが
shutdown
になる前にタスクが完了すると、handleComplete
メソッドが起動され、完了したタスクが消費されます。handleComplete
メソッドは、タスクが結果または例外で完了したときに実行されます。Future
のcancel
メソッドを使用してタスク・スコープが停止される前にタスクを取り消すと、cancel
を起動するスレッドによってhandleComplete
メソッドが実行されます。 タスクの完了または取消しと同時にタスク範囲が停止した場合、handleComplete
メソッドが起動される場合と実行されない場合があります。このタスク範囲がshutdown (またはシャットダウン処理中)の場合、
fork
は、実行されなかったcancelled
タスクを表すFuture
を返します。このメソッドは、タスク・スコープ所有者またはタスク・スコープに含まれるスレッドによってのみ起動できます。 戻された
Future
オブジェクトのcancel
メソッドは、タスク・スコープの所有者またはタスク・スコープに含まれるスレッドに制限されます。cancel
メソッドは、別のスレッドからメソッドはWrongThreadException
をスローします。 返されたFuture
オブジェクト(get
など)上の他のすべてのメソッドは制限されません。- 型パラメータ:
U
- 結果の型- パラメータ:
task
- 実行するタスク- 戻り値:
- 未来
- 例外:
IllegalStateException
- このタスク範囲がクローズされている場合WrongThreadException
- 現在のスレッドが所有者またはタスク範囲に含まれるスレッドではない場合StructureViolationException
- 現在のスコープ値バインディングがタスク・スコープが作成されたときと同じでない場合RejectedExecutionException
- スレッド・ファクトリがタスクを実行するスレッドの作成を拒否した場合
-
join
public StructuredTaskScope<T> join() throws InterruptedExceptionすべてのスレッドが終了するか、タスク範囲がシャットダウンするまで待ちます。 このメソッドは、タスク・スコープで開始されたすべてのスレッドの実行が(タスクとhandleComplete
メソッドの両方)、タスク・スコープを停止するためにshutdown
メソッドが呼び出されるか、現在のスレッドがinterruptedになるまで待機します。このメソッドは、タスク・スコープ所有者のみが起動できます。
- 戻り値:
- このタスクの範囲
- 例外:
IllegalStateException
- このタスク範囲がクローズされている場合WrongThreadException
- 現在のスレッドが所有者でない場合InterruptedException
- 待機中に割込みが発生した場合
-
joinUntil
public StructuredTaskScope<T> joinUntil(Instant deadline) throws InterruptedException, TimeoutException 指定された期限まで、すべてのスレッドが終了するか、タスク範囲がシャットダウンするまで待機します。 このメソッドは、タスク・スコープで開始されたすべてのスレッドの実行が(タスクとhandleComplete
メソッドの両方)、タスク・スコープを停止するためにshutdown
メソッドが呼び出されるか、現在のスレッドがinterruptedになるか、期限に達するまで待機します。このメソッドは、タスク・スコープ所有者のみが起動できます。
- パラメータ:
deadline
- 期限- 戻り値:
- このタスクの範囲
- 例外:
IllegalStateException
- このタスク範囲がクローズされている場合WrongThreadException
- 現在のスレッドが所有者でない場合InterruptedException
- 待機中に割込みが発生した場合TimeoutException
- 待機中に期限に達した場合
-
shutdown
public void shutdown()タスク範囲を閉じずに停止します。 タスク・スコープを停止すると、新しいスレッドが開始され、すべての未完了スレッドが中断され、join
メソッドがウェイ・ク アップされます。 シャットダウンは、未完了のサブタスクの結果が不要になった場合に役立ちます。より具体的には、このメソッドは次のとおりです:
- 結果にスレッド「待っていること」があるタスクを「取消」して、待機中のスレッドがウェイ・ク アップするようにします。
- タスク範囲(現在のスレッドを除く)のすべての未完了スレッドを「割り込み」します。
-
join()
またはjoinUntil(Instant)
で待機している場合、所有者をウェイ・ク アップします。 所有者が待機していない場合、join
またはjoinUntil
への次のコールはすぐに返されます。
このメソッドが完了すると、すべてのタスクの
Future
オブジェクトは、通常または異常なdoneになります。 スレッド割込みに対して(または迅速に応答)に応答しなかったコードを実行しているため、まだ終了していないスレッドがある可能性があります。 このメソッドはこれらのスレッドを待機しません。 所有者がclose
メソッドを起動してタスク・スコープを閉じると、残りのスレッドが終了するまで待機します。このメソッドは、タスク・スコープ所有者またはタスク・スコープに含まれるスレッドによってのみ起動できます。
- 例外:
IllegalStateException
- このタスク範囲がクローズされている場合WrongThreadException
- 現在のスレッドが所有者またはタスク範囲に含まれるスレッドではない場合
-
close
public void close()このタスク範囲を閉じます。このメソッドは、最初にタスク範囲(
shutdown
メソッドを起動した場合と同じ)をシャットダウンします。 その後、未完了のタスクを実行するスレッドが終了するまで待機します。 中断された場合、このメソッドは、中断ステータスを設定して完了する前にスレッドが終了するのを待機し続けます。このメソッドは、タスク・スコープ所有者のみが起動できます。 タスク・スコープがすでにクローズされている場合、このメソッドを起動する所有者は影響しません。
StructuredTaskScope
は、「構造化方式」で使用することを目的としています。 このメソッドをコールして、ネストされたタスク・スコープを閉じる前にタスク・スコープを閉じる場合、ネストされた各タスク・スコープ(作成した逆の順序)の基礎となる構成を閉じ、このタスク・スコープを閉じて、StructureViolationException
をスローします。 同様に、「スコープ値」バインディングで実行中にタスク・スコープを閉じるためにこのメソッドがコールされ、スコープ指定値がバインドされる前にタスク・スコープが作成された場合、タスク・スコープを閉じるとStructureViolationException
がスローされます。 スレッドが最初に所有するタスク・スコープを閉じることなく終了すると、終了によって、そのオープン・タスク・スコープの基礎となる構成がクローズされます。 クローズは、タスク範囲が作成された逆の順序で実行されます。 したがって、所有者がこれらのタスク・スコープでフォークされたスレッドが終了するまで待機する必要がある場合、スレッドの終了が遅延する可能性があります。- 定義:
close
、インタフェースAutoCloseable
- 例外:
IllegalStateException
- 所有者がフォーク後に結合を起動しなかった場合、タスク範囲を閉じた後にスローされますWrongThreadException
- 現在のスレッドが所有者でない場合StructureViolationException
- 構造違反が検出された場合
-