モジュール jdk.incubator.concurrent
パッケージ jdk.incubator.concurrent

クラスScopedValue<T>

java.lang.Object
jdk.incubator.concurrent.ScopedValue<T>
型パラメータ:
T - このScopedValueにバインドされたオブジェクトのタイプ

public final class ScopedValue<T> extends Object
1回設定され、スレッドによる制限付き実行期間の読取りに使用できる値。 ScopedValueを使用すると、データをメソッド引数として渡すことなく、制限された実行期間のデータを安全かつ効率的に共有できます。

ScopedValueは、実行可能なrunメソッドのスレッドによってバウンスされた実行期間のScopedValueの値を設定するwhere(ScopedValue, Object, Runnable)メソッドを定義します。 runによって実行されるメソッドの展開実行では、動的スコープが定義されます。 動的スコープでの実行中、スコープ値はboundで、runメソッドが(通常または例外あり)を完了すると「バインドなし」に戻ります。 動的スコープで実行されるコードは、ScopedValue getメソッドを使用してその値を読み取ります。

「スレッド固有変数」と同様に、スコープ値にはスレッドごとに1つずつ複数のインカネーションがあります。 使用される特定のインカネーションは、そのメソッドを呼び出すスレッドによって異なります。

doSomething()を起動する実行メソッドのスレッドによる実行の値"duke"に対するboundのスコープ値USERNAMEを持つ次の例を考えてみます。

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

    ScopedValue.where(USERNAME, "duke", () -> doSomething());
USERNAME.get()を呼び出すdoSomething()によって直接または間接的に実行されるコードは、値"duke"を読み取ります。 スコープ値は、doSomething()の実行中にバインドされ、doSomething()が(通常または例外あり)を完了するとバインド解除されます。 あるスレッドが"duke1"にバインドされたUSERNAME doSomething()をコールし、別のスレッドが"duke2"にバインドされたUSERNAMEを使用してメソッドをコールする場合、USERNAME.get()は、実行中のスレッドに応じて値"duke1"または"duke2"を読み取ります。

runメソッドを実行するwhereメソッドに加えて、 ScopedValueは、結果を返すメソッドを実行するwhere(ScopedValue, Object, Callable)メソッドを定義します。 また、値へのScopedValueのマッピングを累積するのに役立つ場合にwhere(ScopedValue, Object)メソッドを定義します。

ScopedValueは通常、finalおよび staticフィールドで宣言されます。 フィールドのアクセシビリティにより、その値をバインドまたは読取りできるコンポーネントが決まります。

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

Rebinding

ScopedValue APIを使用すると、「ネストされた動的スコープ」に新しいバインディングを確立できます。 これは「再バインド」と呼ばれます。 ある値にバインドされているScopedValueは、一部のメソッドの制限付き実行の新しい値にバインドされる場合があります。 そのメソッドによって実行されるコードの展開実行によって、ネストされた動的スコープが定義されます。 メソッドが(通常または例外あり)を完了すると、ScopedValueの値は以前の値に戻ります。

前述の例では、doSomething()によって実行されるコードが、次を使用してUSERNAMEを新しい値にバインドするとします:

    ScopedValue.where(USERNAME, "duchess", () -> doMore());
USERNAME.get()を呼び出すdoMore()によって直接または間接的に実行されるコードは、値"duchess"を読み取ります。 doMore()が(通常または例外あり)を完了すると、USERNAMEの値は"duke"に戻ります。

Inheritance

ScopedValueは、スレッド間でのデータの共有をサポートします。 この共有は、親スレッドによる制限された実行期間内に子スレッドが開始および終了する構造化されたケースに制限されます。 具体的には、StructuredTaskScopeを使用する場合、スコープ値バインディングはStructuredTaskScopeの作成時に「取得済」となり、forkメソッドを使用してそのスコープで開始されたすべてのスレッドによって継承されます。

次の例では、ScopedValue USERNAMEは、実行可能操作の実行のために値"duke"にバインドされます。 runメソッドのコードは、StructuredTaskScopeを作成し、3つの子スレッドをフォークします。 childTask1()childTask2()およびchildTask3()を実行しているこれらのスレッドによって直接または間接的に実行されるコードは、値"duke"を読み取ります。

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

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

            scope.fork(() -> childTask1());
            scope.fork(() -> childTask2());
            scope.fork(() -> childTask3());

            ...
         }
    });

実装上のノート:
スコープ値は、かなり小さい数値で使用するように設計されています。get()は最初にスコープを囲んで検索を実行し、スコープ値の最も内側のバインディングを見つけます。 次に、検索の結果が小さいスレッド・ローカル・キャッシュにキャッシュされます。 そのスコープ値に対するget()の以降の呼出しは、ほとんどの場合非常に高速です。 ただし、プログラムに周期的に使用するスコープ値が多数ある場合、キャッシュ・ヒット率は低くなり、パフォーマンスは低下します。 この設計により、StructuredTaskScopeスレッドによるスコープ値継承が非常に高速になります: 本質的には、ポインタをコピーし、スコープ値バインディングを残すだけでも、ポインタを更新するよりはほとんど必要ありません。

スレッドごとのスコープ値キャッシュは小さいため、クライアントは使用中のバインドされたスコープ値の数を最小限に抑える必要があります。 たとえば、この方法で多数の値を渡す必要がある場合、これらの値を保持するレコード・クラスを作成し、そのレコードのインスタンスに単一のScopedValueをバインドすることが理にかなっています。

このベータ・リリースでは、リファレンス実装には、スコープ値のパフォーマンスを調整するためのいくつかのシステム・プロパティが用意されています。

システム・プロパティjdk.incubator.concurrent.ScopedValue.cacheSizeは、(per-thread)スコープ値キャッシュのサイズを制御します。 このキャッシュは、スコープ値のパフォーマンスに不可欠です。 小さすぎる場合、ランタイム・ライブラリはget()ごとに繰り返しスキャンする必要があります。 大きすぎる場合、メモリーは不必要に消費されます。 デフォルトのスコープ値キャッシュ・サイズは16エントリです。 サイズは2 ~ 16エントリに変更できます。 ScopedValue.cacheSizeは2の整数累乗である必要があります。

たとえば、-Djdk.incubator.concurrent.ScopedValue.cacheSize=8を使用できます。

その他のシステム・プロパティはjdk.preserveScopedValueCacheです。 このプロパティは、仮想スレッドがブロックされたときにスレッドごとのスコープ値キャッシュが保持されるかどうかを決定します。 デフォルトでは、このプロパティはtrueに設定されています。つまり、すべての仮想スレッドがブロックされたときにスコープ値キャッシュを保持します。 ScopedValue.cacheSizeと同様、これはスペースと速度のトレードオフです: 多くの仮想スレッドがほとんどブロックされている場合、このプロパティをfalseに設定するとメモリーを節約できますが、各仮想スレッドのスコープ値キャッシュはブロック操作後に再生成する必要があります。

導入されたバージョン:
20
  • ネストされたクラスのサマリー

    ネストされたクラス
    修飾子と型
    クラス
    説明
    static final class 
    「キー」のようなスコープ値の値へのマッピング。
  • メソッドのサマリー

    修飾子と型
    メソッド
    説明
    get()
    現在のスレッドにバインドされている場合、スコープ値の値を返します。
    boolean
    このスコープ値が現在のスレッドにバインドされている場合は、trueを返します。
    static <T> ScopedValue<T>
    すべてのスレッドに対して最初はバインドされていないスコープ値を作成します。
    orElse(T other)
    現在のスレッドにバインドされている場合、このスコープ値の値を返します。それ以外の場合は、otherを返します。
    <X extends Throwable>
    T
    orElseThrow(Supplier<? extends X> exceptionSupplier)
    現在のスレッドにバインドされている場合、このスコープ値の値を返します。それ以外の場合は、例外指定関数によって生成された例外をスローします。
    where(ScopedValue<T> key, T value)
    ScopedValue keyから値への単一のマッピングを使用して、新しいCarrierを作成します。
    static <T> void
    where(ScopedValue<T> key, T value, Runnable op)
    現在のスレッドの値にバインドされたScopedValueを使用して操作を実行します。
    static <T, R> R
    where(ScopedValue<T> key, T value, Callable<? extends R> op)
    現在のスレッドの値にバインドされたScopedValueを使用して、値を返す操作をコールします。

    クラス java.lang.Objectで宣言されたメソッド

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • メソッドの詳細

    • where

      public static <T> ScopedValue.Carrier where(ScopedValue<T> key, T value)
      ScopedValue keyから値への単一のマッピングを使用して、新しいCarrierを作成します。 Carrierを使用してマッピングを蓄積できるため、マッピング内のすべてのスコープ値を値にバインドして操作を実行できます。 次の例では、k1が(またはリ・バウンド)をv1にバインドし、k2が(またはリ・バウンド)をv2にバインドした操作を実行します。
          ScopedValue.where(k1, v1).where(k2, v2).run(() -> ... );
      
      型パラメータ:
      T - 値の型
      パラメータ:
      key - ScopedValueキー
      value - 値はnullです
      戻り値:
      単一のマッピングを持つ新しいCarrier
    • where

      public static <T, R> R where(ScopedValue<T> key, T value, Callable<? extends R> op) throws Exception
      現在のスレッドの値にバインドされたScopedValueを使用して、値を返す操作をコールします。 操作が(通常または例外あり)を完了すると、ScopedValueはバインドされていない状態に戻されるか、現在のスレッドで以前バインドされていた場合は以前の値に戻されます。

      スコープ値は、「構造化方式」で使用することを目的としています。 opによってStructuredTaskScopeが作成され、「閉じる」が作成されない場合、opを終了すると、動的スコープで作成された各StructuredTaskScopeの基礎となる構成がクローズされます。 すべての子スレッドがサブタスクを完了するまで、ブロックが必要になる場合があります。 クローズは、作成された順序とは逆の順序で実行されます。 クローズすると、StructureViolationExceptionがスローされます。

      実装上のノート:
      このメソッドは、次のように実装されます:
          ScopedValue.where(key, value).call(op);
      
      型パラメータ:
      T - 値の型
      R - 結果の型
      パラメータ:
      key - ScopedValueキー
      value - 値はnullです
      op - コールする操作
      戻り値:
      結果
      例外:
      Exception - 操作が例外ありで完了した場合
    • where

      public static <T> void where(ScopedValue<T> key, T value, Runnable op)
      現在のスレッドの値にバインドされたScopedValueを使用して操作を実行します。 操作が(通常または例外あり)を完了すると、ScopedValueはバインドされていない状態に戻されるか、現在のスレッドで以前バインドされていた場合は以前の値に戻されます。

      スコープ値は、「構造化方式」で使用することを目的としています。 opによってStructuredTaskScopeが作成され、「閉じる」が作成されない場合、opを終了すると、動的スコープで作成された各StructuredTaskScopeの基礎となる構成がクローズされます。 すべての子スレッドがサブタスクを完了するまで、ブロックが必要になる場合があります。 クローズは、作成された順序とは逆の順序で実行されます。 クローズすると、StructureViolationExceptionがスローされます。

      実装上のノート:
      このメソッドは、次のように実装されます:
          ScopedValue.where(key, value).run(op);
      
      型パラメータ:
      T - 値の型
      パラメータ:
      key - ScopedValueキー
      value - 値はnullです
      op - コールする操作
    • newInstance

      public static <T> ScopedValue<T> newInstance()
      すべてのスレッドに対して最初はバインドされていないスコープ値を作成します。
      型パラメータ:
      T - 値の型
      戻り値:
      新しいScopedValue
    • get

      public T get()
      現在のスレッドにバインドされている場合、スコープ値の値を返します。
      戻り値:
      現在のスレッドにバインドされている場合のスコープ値の値
      例外:
      NoSuchElementException - スコープ値がバインドされていない場合
    • isBound

      public boolean isBound()
      このスコープ値が現在のスレッドにバインドされている場合は、trueを返します。
      戻り値:
      このスコープ値が現在のスレッドにバインドされている場合、true
    • orElse

      public T orElse(T other)
      現在のスレッドにバインドされている場合、このスコープ値の値を返します。それ以外の場合は、otherを返します。
      パラメータ:
      other - バインドされていない場合に返される値は、nullです
      戻り値:
      バインドされている場合はスコープ値、そうでない場合はother
    • orElseThrow

      public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X
      現在のスレッドにバインドされている場合、このスコープ値の値を返します。それ以外の場合は、例外指定関数によって生成された例外をスローします。
      型パラメータ:
      X - スローされる可能性がある例外のタイプ
      パラメータ:
      exceptionSupplier - スローする例外を生成する供給関数
      戻り値:
      現在のスレッドにバインドされている場合のスコープ値の値
      例外:
      X - スコープ値が現在のスレッドにバインドされていない場合