モジュール java.base
パッケージ java.util.concurrent

クラスStructuredTaskScope<T>

java.lang.Object
java.util.concurrent.StructuredTaskScope<T>
型パラメータ:
T - タスク・スコープで実行されるタスクの結果タイプ
すべての実装されたインタフェース:
AutoCloseable
直系の既知のサブクラス:
StructuredTaskScope.ShutdownOnFailurePREVIEW, StructuredTaskScope.ShutdownOnSuccessPREVIEW

public class StructuredTaskScope<T> extends Object implements AutoCloseable
StructuredTaskScopeは、JavaプラットフォームのプレビューAPIです。
プレビュー機能が有効な場合のみ、プログラムでStructuredTaskScopeを使用できます。
プレビュー機能は、今後のリリースで削除するか、Javaプラットフォームの永続機能にアップグレードすることができます。
「構造化並行性」の基本API。 StructuredTaskScopeでは、タスクが複数の同時サブタスクに分割され、メイン・タスクが続行される前にサブタスクが完了する必要があるケースがサポートされます。 StructuredTaskScopeを使用すると、構造化プログラミングの順次演算と同様に、同時演算の存続期間が「構文ブロック」によって制限されるようにできます。

基本操作

StructuredTaskScopeは、そのパブリック・コンストラクタのいずれかを使用して作成されます。 サブタスクを実行するスレッドを開始するforkメソッド、すべてのサブタスクが終了するのを待機するjoinメソッド、およびタスク・スコープをクローズするcloseメソッドを定義します。 APIは、 try-with-resources文で使用することを目的としています。 この意図は、try blockのコードがforkメソッドを使用してスレッドをフォークしてサブタスクを実行し、joinメソッドでサブタスクが終了するまで待機し、「結果を処理」を使用することです。 forkメソッドをコールすると、「フォークされたサブタスク」を表すSubtaskPREVIEWが返されます。 joinがコールされると、Subtaskを使用して、結果が正常に完了したり、サブタスクが失敗した場合は例外を取得できます。
    Callable<String> task1 = ...
    Callable<Integer> task2 = ...

    try (var scope = new StructuredTaskScope<Object>()) {

        Subtask<String> subtask1 = scope.fork(task1);
        Subtask<Integer> subtask2 = scope.fork(task2);

        scope.join();

        ... process results/exceptions ...

    } // close

次の例では、同種のサブタスクのコレクションをフォークし、すべてのサブタスクがjoinメソッドで完了するのを待機し、Subtask.StatePREVIEWを使用して、正常に完了したサブタスクのセットと、失敗したサブタスクの別のサブタスクにサブタスクをパーティション化します。

    List<Callable<String>> callables = ...

    try (var scope = new StructuredTaskScope<String>()) {

        List<Subtask<String>> subtasks = callables.stream().map(scope::fork).toList();

        scope.join();

        Map<Boolean, Set<Subtask<String>>> map = subtasks.stream()
                .collect(Collectors.partitioningBy(h -> h.state() == Subtask.State.SUCCESS,
                                                   Collectors.toSet()));

    } // close

正しい使用を確実にするために、joinおよびcloseメソッドはowner (タスク・スコープを開いたり作成したスレッド)によってのみ起動でき、所有者がフォーク後にjoinメソッドを呼び出さなかった場合、closeメソッドはクローズ後に例外をスローします。

StructuredTaskScopeは、タスク・スコープを閉じずに停止するshutdownメソッドを定義します。 shutdown()メソッド「取消」は、すべての未完了サブタスクを「割り込み」によってスレッドに指定します。 新しいスレッドがタスク・スコープで開始されないようにします。 所有者がjoinメソッドで待機している場合は、ウェイクアップします。

シャットダウンはshort-circuitingに使用され、すべてのサブタスクの完了を必要としない「政策」をサブクラスに実装できます。

一般的なケースのポリシーを持つサブクラス

StructuredTaskScopeの2つのサブクラスは、一般的な場合にポリシーを実装するために定義されています:
  1. ShutdownOnSuccessPREVIEWは、正常に完了する最初のサブタスクの結果を取得します。 取得すると、タスク・スコープがシャットダウンされ、未完了のスレッドが中断され、所有者がウェイクアップされます。 このクラスは、サブタスクの結果が("任意の起動")を実行し、他の未完了サブタスクの結果を待機する必要がない場合に対象となります。 最初の結果を取得するメソッド、またはすべてのサブタスクが失敗した場合に例外をスローするメソッドを定義します。
  2. ShutdownOnFailurePREVIEWは、失敗する最初のサブタスクの例外を取得します。 取得すると、タスク・スコープがシャットダウンされ、未完了のスレッドが中断され、所有者がウェイクアップされます。 このクラスは、すべてのサブタスクの結果が("すべて起動")を必要とする場合を対象としています。サブタスクが失敗した場合、他の未完了サブタスクの結果は不要になります。 サブタスクのいずれかが失敗した場合に例外をスローするメソッドを定義する場合。

次に、2つのクラスを使用する2つの例を示します。 どちらの場合も、2つのURLのロケーション"left"および"right"からリソースをフェッチするために、サブタスクのペアがフォークされます。 最初の例では、最初のサブタスクが正常に完了する結果を取得するShutdownOnSuccessオブジェクトを作成し、タスク・スコープを停止することによってもう一方を取消しします。 サブタスクのいずれかが結果で完了するか、両方のサブタスクが失敗するまで、メイン・タスクはjoinで待機します。 result(Function)PREVIEWメソッドを起動して、取得された結果を取得します。 両方のサブタスクが失敗した場合、このメソッドは、原因としていずれかのサブタスクから例外とともに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));

        ...
    }
2番目の例では、失敗する最初のサブタスクの例外を取得するShutdownOnFailureオブジェクトを作成し、タスク範囲を停止してもう一方のサブタスクを取り消します。 メイン・タスクは、両方のサブタスクが完了するまで(失敗するか、期限に達するまで)、joinUntil(Instant)で待機します。 throwIfFailed(Function)PREVIEWを起動して、いずれかのサブタスクが失敗した場合に例外をスローします。 両方のサブタスクが正常に完了した場合、このメソッドはno-opです。 この例では、Supplier.get()を使用して各サブタスクの結果を取得します。 一般的に、forkによって返されるオブジェクトが、正常に完了したサブタスクの結果を取得するためにのみ使用される場合には、SubtaskのかわりにSupplierを使用することをお薦めします。
   Instant deadline = ...

   try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {

        Supplier<String> supplier1 = scope.fork(() -> query(left));
        Supplier<String> supplier2 = scope.fork(() -> query(right));

        scope.joinUntil(deadline);

        scope.throwIfFailed(e -> new WebApplicationException(e));

        // both subtasks completed successfully
        String result = Stream.of(supplier1, supplier2)
                .map(Supplier::get)
                .collect(Collectors.joining(", ", "{ ", " }"));

        ...
    }

StructuredTaskScopeの拡張

StructuredTaskScopeを拡張し、handleCompleteメソッドをオーバーライドして、ShutdownOnSuccessおよびShutdownOnFailureによって実装されたポリシー以外のポリシーを実装できます。 たとえば、サブクラスは、正常に完了したサブタスクの結果を収集し、失敗したサブタスクを無視する場合があります。 サブタスクが失敗した場合に例外が収集される場合があります。 shutdownメソッドを起動して停止し、一部の条件が発生したときにjoinがウェイクアップする場合があります。

サブクラスは、通常、joinメソッドの後に実行されるコードに対して使用可能な結果、状態またはその他の結果を作成するメソッドを定義します。 結果を収集し、失敗したサブタスクを無視するサブクラスでは、結果を返すメソッドを定義できます。 サブタスクが失敗したときに停止するポリシーを実装するサブクラスは、失敗する最初のサブタスクの例外を取得するメソッドを定義できます。

次に、正常に完了した同種のサブタスクを収集する単純なStructuredTaskScope実装の例を示します。 メイン・タスクが結合後に起動できるメソッド"completedSuccessfully()"を定義します。

    class CollectingScope<T> extends StructuredTaskScope<T> {
        private final Queue<Subtask<? extends T>> subtasks = new LinkedTransferQueue<>();

        @Override
        protected void handleComplete(Subtask<? extends T> subtask) {
            if (subtask.state() == Subtask.State.SUCCESS) {
                subtasks.add(subtask);
            }
        }

        @Override
        public CollectingScope<T> join() throws InterruptedException {
            super.join();
            return this;
        }

        public Stream<Subtask<? extends T>> completedSuccessfully() {
            super.ensureOwnerAndJoined();
            return subtasks.stream();
        }
    }

この例のcompletedSuccessfully()メソッドの実装では、ensureOwnerAndJoined()を起動して、メソッドが結合された後にのみ所有者スレッドによってのみ呼び出されるようにします。

ツリー構造

タスク・スコープは、新しいタスク・スコープを開いたときに親子関係が暗黙的に確立されるツリーを形成します:
  • 親子関係は、タスク・スコープで開始されたスレッドが独自のタスク・スコープを開くと確立されます。 タスク・スコープ"A"で開始されたスレッドで、タスク・スコープ"B"を開くと、タスク・スコープ"A"がタスク・スコープ"B"の親である親子関係が確立されます。
  • 親子関係はネストによって確立されます。 スレッドがタスク・スコープ"B"を開き、タスク・スコープ"C" ("B"をクローズする前に)を開くと、包含タスク・スコープ"B"はネストされたタスク・スコープ"C"の親になります。
タスク・スコープのdescendantsは、親である子タスク・スコープと、再帰的に子タスク・スコープの子孫です。

ツリー構造は次のものをサポートします:

  • スレッド間での「スコープ値」PREVIEWの継承。
  • 確認チェック。 メソッド記述の"タスク範囲に含まれるスレッド"というフレーズは、タスク・スコープまたは子孫スコープで開始されたスレッドを意味します。

次の例は、スコープ値の継承を示しています。 スコープ値USERNAMEは、値"duke"にバインドされます。 StructuredTaskScopeが作成され、 childTaskを実行するスレッドを起動するためにそのforkメソッドが起動されます。 スレッドは、タスク・スコープの作成時に取得されたスコープ値「バインド」を継承します。 childTaskのコードは、スコープ値の値を使用するため、値"duke"を読み取ります。

    private static final ScopedValue<String> USERNAME = ScopedValue.newInstance();

    ScopedValue.runWhere(USERNAME, "duke", () -> {
        try (var scope = new StructuredTaskScope<String>()) {

            scope.fork(() -> childTask());
            ...
         }
    });

    ...

    String childTask() {
        String name = USERNAME.get();   // "duke"
        ...
    }

StructuredTaskScopeでは、現時点ではツリー構造を公開するAPIは定義されません。

特に指定しないかぎり、このクラスのコンストラクタまたはメソッドにnull引数を渡すと、NullPointerExceptionがスローされます。

メモリーの一貫性の影響

サブタスクhappen-before「フォーク」より前のタスク・スコープの所有者スレッドまたは含まれるスレッドでのアクション。このサブタスクによって実行されるアクションは、happen-beforeで、サブタスクの結果は「取得済」PREVIEWまたはhappen-beforeで、タスク・スコープのjoiningの後にスレッドで実行されるアクションです。

Java言語仕様を参照してください:
17.4.5 Happens-beforeオーダー
導入されたバージョン:
21
  • コンストラクタの詳細

    • StructuredTaskScope

      public StructuredTaskScope(String name, ThreadFactory factory)
      指定された名前とスレッド・ファクトリを使用して構造化タスク・スコープを作成します。 タスク範囲には、モニタリングおよび管理の目的で任意の名前が付けられます。 スレッド・ファクトリは、サブタスクが「フォーク」の場合にcreateスレッドに使用されます。 タスク・スコープは現在のスレッドによって所有されます。

      Constructionは、現在のスレッドの「スコープ値」PREVIEWバインディングを取得して、タスク・スコープで開始されたスレッドによる継承を取得します。 クラス説明の「ツリー構造」セクションには、スコープ値バインディングの継承を目的として、親子関係が暗黙的に確立される方法の詳細が示されています。

      パラメータ:
      name - タスク範囲の名前。NULLにできます
      factory - スレッドのファクトリ
    • StructuredTaskScope

      public StructuredTaskScope()
      仮想スレッドを作成する名前のない構造化タスク・スコープを作成します。 タスク・スコープは現在のスレッドによって所有されます。
      実装要件:
      このコンストラクタは、nullという名前の2引数コンストラクタと、仮想スレッドを作成するスレッド・ファクトリの起動と同等です。
  • メソッドの詳細

    • ensureOwnerAndJoined

      protected final void ensureOwnerAndJoined()
      現在のスレッドがこのタスク・スコープの所有者であること、および「フォーク」サブタスクの後に(join()またはjoinUntil(Instant))を結合したことを確認します。
      APIのノート:
      このメソッドは、結合メソッドの後に実行するコードに対して、使用可能な結果、状態またはその他の結果を生成するメソッドを定義するサブクラスで使用できます。
      例外:
      WrongThreadException - 現在のスレッドがタスク・スコープ所有者でない場合
      IllegalStateException - タスク・スコープがオープンで、タスク・スコープ所有者がフォーク後に結合しなかった場合
    • handleComplete

      protected void handleComplete(StructuredTaskScope.SubtaskPREVIEW<? extends T> subtask)
      このタスク・スコープで正常に完了または失敗したときに、サブタスクによって起動されます。 このメソッドは、タスク・スコープが「停止」の後にサブタスクが完了した場合には起動されません。
      APIのノート:
      handleCompleteメソッドはスレッド・セーフである必要があります。 複数のスレッドによって同時に起動できます。
      実装要件:
      サブタスクがnullの場合、デフォルトの実装ではNullPointerExceptionがスローされます。 サブタスクが完了していない場合、IllegalArgumentExceptionがスローされます。
      パラメータ:
      subtask - サブタスク
      例外:
      IllegalArgumentException - 完了していないサブタスクでコールされた場合
    • fork

      public <U extends T> StructuredTaskScope.SubtaskPREVIEW<U> fork(Callable<? extends U> task)
      このタスク・スコープで新しいスレッドを開始して、値を戻すタスクを実行し、このタスク・スコープの「サブタスク」を作成します。

      値を返すタスクは、このメソッドにCallableとして提供され、スレッドはタスクのcallメソッドを実行します。 スレッドは、タスク・スコープのThreadFactoryを使用して作成されます。 現在のスレッドの「スコープ値」PREVIEWバインディングを継承します。 バインディングは、タスク・スコープの作成時に取得されたバインディングと一致する必要があります。

      このメソッドは、「フォークされたサブタスク」を表すSubtaskPREVIEWを返します。 Subtaskオブジェクトを使用すると、サブタスクが正常に完了したとき、またはサブタスクが失敗したときの例外を取得できます。 正しい使用を確実にするために、get()PREVIEWおよびexception()PREVIEWメソッドは、すべてのスレッドがjoinまたはjoinUntil(Instant)メソッドで終了するまで待機した後でのみ、タスク・スコープ所有者によってコールできます。 サブタスクが完了すると、スレッドはhandleCompleteメソッドを呼び出して、完了したサブタスクを消費します。 サブタスクが完了する前にタスク・スコープが「停止」の場合、handleCompleteメソッドは起動されません。

      このタスク・スコープがshutdown (またはシャットダウン処理中)の場合、サブタスクは実行されず、handleCompleteメソッドは起動されません。

      このメソッドは、タスク・スコープ所有者またはタスク・スコープに含まれるスレッドによってのみ起動できます。

      実装要件:
      このメソッドは、たとえばラップ・タスクなど、カスタマイズの目的でオーバーライドできます。 オーバーライドされた場合、サブクラスはsuper.forkを呼び出して、このタスク・スコープで新しいスレッドを開始する必要があります。
      型パラメータ:
      U - 結果の型
      パラメータ:
      task - 実行するスレッドの値を返すタスク
      戻り値:
      サブタスク
      例外:
      IllegalStateException - このタスク範囲がクローズされている場合
      WrongThreadException - 現在のスレッドがタスク・スコープの所有者またはタスク・スコープに含まれるスレッドでない場合
      StructureViolationExceptionPREVIEW - 現在のスコープ値バインディングがタスク・スコープが作成されたときと同じでない場合
      RejectedExecutionException - スレッド・ファクトリがサブタスクを実行するスレッドの作成を拒否した場合
    • join

      このタスク・スコープで開始されたすべてのサブタスクが終了するか、タスク・スコープが停止するまで待機します。

      このメソッドは、このタスク・スコープのすべてのスレッドstartedが実行の完了を待機して、すべてのサブタスクを待機します。 すべてのスレッドが終了したとき、タスク・スコープが「停止」の場合、または現在のスレッドがinterruptedの場合、待機が停止します。

      このメソッドは、タスク・スコープ所有者のみが起動できます。

      実装要件:
      このメソッドは、カスタマイズの目的で上書きしたり、より具体的な戻りタイプを返すことができます。 オーバーライドされた場合、サブクラスは super.joinを呼び出して、メソッドがこのタスク・スコープのスレッドが終了するまで待機する必要があります。
      戻り値:
      このタスクの範囲
      例外:
      IllegalStateException - このタスク範囲がクローズされている場合
      WrongThreadException - 現在のスレッドがタスク・スコープ所有者でない場合
      InterruptedException - 待機中に割込みが発生した場合
    • joinUntil

      このタスク・スコープで開始されたすべてのサブタスクが終了するまで、またはタスク・スコープが停止するまで、指定した期限まで待機します。

      このメソッドは、このタスク・スコープのすべてのスレッドstartedが実行の完了を待機して、すべてのサブタスクを待機します。 すべてのスレッドが終了するか、タスク・スコープが「停止」、期限に達するか、現在のスレッドがinterruptedになると、待機が停止します。

      このメソッドは、タスク・スコープ所有者のみが起動できます。

      実装要件:
      このメソッドは、カスタマイズの目的で上書きしたり、より具体的な戻りタイプを返すことができます。 オーバーライドされた場合、サブクラスは super.joinUntilを呼び出して、メソッドがこのタスク・スコープのスレッドが終了するまで待機する必要があります。
      パラメータ:
      deadline - 期限
      戻り値:
      このタスクの範囲
      例外:
      IllegalStateException - このタスク範囲がクローズされている場合
      WrongThreadException - 現在のスレッドがタスク・スコープ所有者でない場合
      InterruptedException - 待機中に割込みが発生した場合
      TimeoutException - 待機中に期限に達した場合
    • shutdown

      public void shutdown()
      このタスク・スコープをクローズせずに停止します。 タスク・スコープを停止すると、新しいスレッドが開始され、すべての未完了スレッドが中断され、joinメソッドがウェイクアップされます。 シャットダウンは、未完了のサブタスクの結果が不要になった場合に役立ちます。 これは通常、結果に達した後、未完了のタスクを破棄するポリシーを実装するサブクラスのhandleComplete(Subtask)実装によってコールされます。

      より具体的には、このメソッドは次のとおりです:

      • タスク範囲(現在のスレッドを除く)のすべての未完了スレッドを「割り込み」します。
      • join()またはjoinUntil(Instant)で待機している場合、タスク・スコープの所有者を起動します。 タスク・スコープの所有者が待機していない場合、joinまたはjoinUntilへの次のコールはすぐに返されます。

      このメソッドは、タスク・スコープ所有者またはタスク・スコープに含まれるスレッドによってのみ起動できます。

      APIのノート:
      スレッド割り込みに (または迅速に応答)を応答しなかったコードを実行しているために、終了していないスレッドがある可能性があります。 このメソッドはこれらのスレッドを待機しません。 所有者がcloseメソッドを起動してタスク・スコープを閉じると、残りのスレッドが終了するまで待機します。
      実装要件:
      このメソッドは、カスタマイズの目的で上書きできます。 オーバーライドされた場合、サブクラスはsuper.shutdownを起動して、メソッドがタスク・スコープを停止するようにする必要があります。
      例外:
      IllegalStateException - このタスク範囲がクローズされている場合
      WrongThreadException - 現在のスレッドがタスク・スコープの所有者またはタスク・スコープに含まれるスレッドでない場合
      関連項目:
    • isShutdown

      public final boolean isShutdown()
      このタスク・スコープがシャットダウンされている場合はtrueを返し、それ以外の場合はfalseを返します。
      戻り値:
      このタスク・スコープが停止した場合はtrue、停止しない場合はfalse
      関連項目:
    • close

      public void close()
      このタスク範囲を閉じます。

      このメソッドは、最初にタスク範囲(shutdownメソッドを起動した場合と同じ)をシャットダウンします。 その後、未完了のタスクを実行するスレッドが終了するまで待機します。 中断された場合、このメソッドは、中断ステータスが設定された状態で完了するまで、スレッドが終了するまで待機し続けます。

      このメソッドは、タスク・スコープ所有者のみが起動できます。 タスク・スコープがすでにクローズされている場合、このメソッドを起動するタスク・スコープ所有者は無効です。

      StructuredTaskScopeは、「構造化方式」で使用することを目的としています。 このメソッドが呼び出されて、ネストされたタスク・スコープがクローズされる前にタスク・スコープをクローズすると、ネストされた各タスク・スコープ(作成した逆の順序)の基礎となる構成がクローズされ、このタスク・スコープがクローズされて、StructureViolationExceptionPREVIEWがスローされます。 同様に、「スコープ値」PREVIEWバインディングで実行中にタスク・スコープを閉じるためにこのメソッドがコールされ、スコープ値がバインドされる前にタスク・スコープが作成された場合、タスク・スコープを閉じると、StructureViolationExceptionがスローされます。 スレッドが最初に所有するタスク・スコープを閉じることなく終了すると、終了によって、そのオープン・タスク・スコープの基礎となる構成がクローズされます。 クローズは、タスク範囲が作成された逆の順序で実行されます。 したがって、タスク・スコープの所有者が、これらのタスク・スコープでフォークされたスレッドが終了するまで待機する必要がある場合、スレッドの終了が遅延する可能性があります。

      定義:
      close、インタフェースAutoCloseable
      実装要件:
      このメソッドは、カスタマイズの目的で上書きできます。 オーバーライドされた場合、サブクラスはsuper.closeを呼び出してタスク・スコープを閉じる必要があります。
      例外:
      IllegalStateException - タスク・スコープの所有者がフォーク後に結合を試行しなかった場合、タスク・スコープのクローズ後にスローされました
      WrongThreadException - 現在のスレッドがタスク・スコープ所有者でない場合
      StructureViolationExceptionPREVIEW - 構造違反が検出された場合