モジュール java.base
パッケージ java.lang

クラスScopedValue<T>

java.lang.Object
java.lang.ScopedValue<T>
型パラメータ:
T - 値の型

public final class ScopedValue<T> extends Object
ScopedValueは、JavaプラットフォームのプレビューAPIです。
プレビュー機能が有効な場合のみ、プログラムでScopedValueを使用できます。
プレビュー機能は、今後のリリースで削除するか、Javaプラットフォームの永続機能にアップグレードすることができます。
メソッド・パラメータを使用せずに、メソッドに安全かつ効率的に共有できる値。

Javaプログラミング言語では、データは通常、メソッド・パラメータを使用してメソッドに渡されます。 データを使用するメソッドを取得するには、様々なメソッドのシーケンスでデータを渡す必要があります。 呼出しのシーケンス内のすべてのメソッドがパラメータを宣言する必要があり、すべてのメソッドがデータにアクセスできます。 ScopedValueは、メソッド・パラメータを使用せずに遠隔地メソッド(通常、「コールバック」)にデータを渡す手段を提供します。 実際には、ScopedValue「暗黙的なメソッド・パラメータ」です。 "かのように"は、一連の呼出しのすべてのメソッドに追加パラメータを持ちます。 どのメソッドもパラメータを宣言せず、ScopedValueオブジェクトにアクセスできるメソッドのみがその値(データ)にアクセスできます。 ScopedValueを使用すると、データのパラメータを宣言せず、データにアクセスできない一連の中間メソッドを介して、「発信者」から遠隔地「逃す」にデータを安全に渡すことができます。

ScopedValue APIは、ScopedValueオブジェクトboundを持つメソッドを、メソッドの限界実行期間の値に対して実行することで機能します。 このメソッドは別のメソッドを呼び出すことができ、別のメソッドを呼び出すことができます。 メソッドの展開実行では、「動的スコープ」を定義します。 ScopedValueオブジェクトにアクセスできるこれらのメソッドのコードは、その値を読み取ることができます。 元のメソッドが正常に完了した場合、または例外が発生した場合、ScopedValueオブジェクトは「バインドなし」に戻ります。 ScopedValue APIでは、値にバインドされたScopedValueを使用して、Runnable.runCallable.callまたはSupplier.getメソッドの実行がサポートされます。

runメソッドを実行するために、値"duke"にバインドされたスコープ値"NAME"を持つ次の例について考えてみます。 runメソッドは、次にdoSomethingを呼び出します。

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

    ScopedValue.runWhere(NAME, "duke", () -> doSomething());
doSomethingによって直接または間接的に実行され、フィールドNAMEにアクセスするコードは、NAME.get()を呼び出して値"duke"を読み取ることができます。 NAME は、runメソッドの実行中にバインドされます。 runメソッドが完了すると、バインド解除に戻ります。

runWhereを使用する例では、結果を返さないメソッドを呼び出します。 callWhereおよびgetWhereを使用して、結果を返すメソッドを起動できます。 また、ScopedValueは、すべてのScopedValueが値にバインドされたメソッドをコールする前に、複数のマッピング(値へのScopedValue)が累積される場合にwhere(ScopedValue, Object)メソッドを定義します。

バインディングはスレッド単位

値へのScopedValueバインディングはスレッド単位です。 xxxWhereを呼び出すと、現在のスレッドの値にバインドされたScopedValueを使用してメソッドが実行されます。 getメソッドは、現在のスレッドにバインドされた値を返します。

この例では、1つのスレッドによって実行されるコードが次を呼び出します:

    ScopedValue.runWhere(NAME, "duke1", () -> doSomething());
そして、別のスレッドによって実行されたコードは次を呼び出します:
    ScopedValue.runWhere(NAME, "duke2", () -> doSomething());
NAME.get()を起動するdoSomething (またはコールするメソッド)のコードは、実行中のスレッドに応じて、値"duke1"または"duke2"を読み取ります。

機能としてのスコープ値

ScopedValueオブジェクトは、ScopedValueがバインドされるときに、その値にアクセスするためのcapabilityまたはキーとして扱う必要があります。 セキュアな使用方法は、アクセス制御(「Java Virtual Machine仕様」、セクション5.4.4を参照してください)に依存し、ScopedValueオブジェクトを共有しないように注意してください。 多くの場合、 ScopedValueは、単一のクラス(またはネスト)のコードのみにアクセスできるように、finalおよびstaticフィールドで宣言されます。

Rebinding

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

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

    ScopedValue.runWhere(NAME, "duchess", () -> doMore());
NAME.get()を起動するdoMore()によって直接または間接的に実行されるコードは、値"duchess"を読み取ります。 doMore()が完了すると、NAMEの値は"duke"に戻ります。

Inheritance

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

スレッド間で共有されるScopedValueでは、値が不変オブジェクトであるか、または値へのすべてのアクセスが適切に同期される必要があります。

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

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

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

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

            ...
         }
    });

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

APIのノート:
ScopedValueは、メソッド・パラメータを使用せずにデータの"一方向伝送"が目標である場合、ThreadLocalより優先される必要があります。 ThreadLocalは、メソッド・パラメータを使用せずにメソッドにデータを渡すために使用できますが、次のような多くの問題が発生します:
  1. ThreadLocalは、遠距離呼び出し先のコードが新しい値をsettingから防ぐことはありません。
  2. ThreadLocalにはバインドされていない存続期間があるため、明示的にremovedを指定しないかぎり、メソッドの完了後も引き続き値があります。
  3. 「継承」は高価 - 各子スレッドを作成するときに、スレッドと値のマップをコピーする必要があります。
実装上のノート:
スコープ値は、かなり小さい数値で使用するように設計されています。get()は最初にスコープを囲んで検索を実行し、スコープ値の最も内側のバインディングを見つけます。 次に、検索の結果が小さいスレッド・ローカル・キャッシュにキャッシュされます。 そのスコープ値に対するget()の以降の呼出しは、ほとんどの場合非常に高速です。 ただし、プログラムに周期的に使用するスコープ値が多数ある場合、キャッシュ・ヒット率は低くなり、パフォーマンスは低下します。 この設計により、StructuredTaskScopePREVIEWスレッドによるスコープ指定値の継承が非常に高速になります: 本質的には、ポインタをコピーし、スコープ値バインディングを残すだけでも、ポインタを更新するよりはほとんど必要ありません。

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

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

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

たとえば、-Djava.lang.ScopedValue.cacheSize=8を使用できます。

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

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

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

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

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

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

    • where

      public static <T> ScopedValue.CarrierPREVIEW where(ScopedValuePREVIEW<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
    • callWhere

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

      スコープ値は、「構造化方式」で使用することを目的としています。 操作によって直接または間接的に呼び出されたコードでStructuredTaskScopePREVIEWが作成するが、それを「閉じ」PREVIEWない場合、操作が完了すると(通常または例外あり) 「構造違反」として検出されます。 その場合、StructuredTaskScopeの基礎となる構造が閉じられ、StructureViolationExceptionPREVIEWがスローされます。

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

      public static <T, R> R getWhere(ScopedValuePREVIEW<T> key, T value, Supplier<? extends R> op)
      ScopedValueが現在のスレッドの値にバインドされた結果のサプライヤを起動します。 操作が(通常または例外あり)を完了すると、ScopedValueはバインドされていない状態に戻されるか、現在のスレッドで以前バインドされていた場合は以前の値に戻されます。 opが例外ありで完了すると、このメソッドによって伝播されます。

      スコープ値は、「構造化方式」で使用することを目的としています。 操作によって直接または間接的に呼び出されたコードでStructuredTaskScopePREVIEWが作成するが、それを「閉じ」PREVIEWない場合、操作が完了すると(通常または例外あり) 「構造違反」として検出されます。 その場合、StructuredTaskScopeの基礎となる構造が閉じられ、StructureViolationExceptionPREVIEWがスローされます。

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

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

      スコープ値は、「構造化方式」で使用することを目的としています。 操作によって直接または間接的に呼び出されたコードでStructuredTaskScopePREVIEWが作成するが、それを「閉じ」PREVIEWない場合、操作が完了すると(通常または例外あり) 「構造違反」として検出されます。 その場合、StructuredTaskScopeの基礎となる構造が閉じられ、StructureViolationExceptionPREVIEWがスローされます。

      実装上のノート:
      このメソッドは、次のように実装されます:
          ScopedValue.where(key, value).run(op);
      
      型パラメータ:
      T - 値の型
      パラメータ:
      key - ScopedValueキー
      value - 値はnullです
      op - コールする操作
      例外:
      StructureViolationExceptionPREVIEW - 構造違反が検出された場合
    • newInstance

      public static <T> ScopedValuePREVIEW<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 - スコープ値が現在のスレッドにバインドされていない場合