クラスScopedValue<T>
- 型パラメータ:
T
- 値の型
ScopedValue
は、JavaプラットフォームのプレビューAPIです。
Javaプログラミング言語では、データは通常、メソッド・パラメータを使用してメソッドに渡されます。 データを使用するメソッドを取得するには、様々なメソッドのシーケンスでデータを渡す必要があります。 呼出しのシーケンス内のすべてのメソッドがパラメータを宣言する必要があり、すべてのメソッドがデータにアクセスできます。 ScopedValue
は、メソッド・パラメータを使用せずに遠隔地メソッド(通常、「コールバック」)にデータを渡す手段を提供します。 実際には、ScopedValue
は「暗黙的なメソッド・パラメータ」です。 "かのように"は、一連の呼出しのすべてのメソッドに追加パラメータを持ちます。 どのメソッドもパラメータを宣言せず、ScopedValue
オブジェクトにアクセスできるメソッドのみがその値(データ)にアクセスできます。 ScopedValue
を使用すると、データのパラメータを宣言せず、データにアクセスできない一連の中間メソッドを介して、「発信者」から遠隔地「逃す」にデータを安全に渡すことができます。
ScopedValue
APIは、ScopedValue
オブジェクトboundを持つメソッドを、メソッドの限界実行期間の値に対して実行することで機能します。 このメソッドは別のメソッドを呼び出すことができ、別のメソッドを呼び出すことができます。 メソッドの展開実行では、「動的スコープ」を定義します。 ScopedValue
オブジェクトにアクセスできるこれらのメソッドのコードは、その値を読み取ることができます。 元のメソッドが正常に完了した場合、または例外が発生した場合、ScopedValue
オブジェクトは「バインドなし」に戻ります。 ScopedValue
APIでは、値にバインドされたScopedValue
を持つRunnable
またはScopedValue.CallableOp
PREVIEWの実行がサポートされています。
Runnable
のrun
メソッドを実行するために、値"duke
"にバインドされたスコープ値"NAME
"を持つ次の例を考えてみます。 次に、run
メソッドは、メソッドdoSomething
を呼び出します。
private static final ScopedValue<String> NAME = ScopedValue.newInstance
();
ScopedValue.where(NAME, "duke").run
(() -> doSomething());
doSomething
によって直接または間接的に実行され、フィールドNAME
にアクセスするコードは、NAME.get()
を呼び出して値"duke
"を読み取ることができます。 NAME
は、run
メソッドの実行中にバインドされます。 run
メソッドが完了すると、バインド解除に戻ります。
run
を使用する例では、結果を返さないメソッドを呼び出します。 call
PREVIEWメソッドを使用すると、結果を返すメソッドを呼び出すことができます。 ScopedValue
は、すべてのScopedValue
が値にバインドされたメソッドをコールする前に、複数のマッピング(値へのScopedValue
)が累積される場合にwhere(ScopedValue, Object)
メソッドを定義します。
バインディングはスレッド単位
値へのScopedValue
バインディングはスレッド単位です。 run
を呼び出すと、現在のスレッドの値にバインドされたScopedValue
を使用してメソッドが実行されます。 get
メソッドは、現在のスレッドにバインドされた値を返します。
この例では、1つのスレッドによって実行されるコードが次を呼び出します:
ScopedValue.where(NAME, "duke1").run(() -> doSomething());
ScopedValue.where(NAME, "duke2").run(() -> 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.where(NAME, "duchess").run(() -> doMore());
NAME.get()
を起動するdoMore()
によって直接または間接的に実行されるコードは、値"duchess
"を読み取ります。 doMore()
が完了すると、NAME
の値は"duke
"に戻ります。
Inheritance
ScopedValue
は、スレッド間での共有をサポートします。 この共有は、親スレッドによる制限された実行期間内に子スレッドが開始および終了する構造化されたケースに制限されます。 StructuredTaskScope
PREVIEWを使用する場合、StructuredTaskScope
の作成時にスコープ値バインディングが「取得済」になり、fork
PREVIEWメソッドを使用してそのタスク・スコープで開始されたすべてのスレッドによって継承されます。
スレッド間で共有されるScopedValue
では、値が不変オブジェクトであるか、または値へのすべてのアクセスが適切に同期される必要があります。
次の例では、ScopedValue
NAME
は、実行可能操作の実行のために値"duke
"にバインドされます。 run
メソッドのコードは、3つのタスクをフォークするStructuredTaskScope
を作成します。 childTask1()
、childTask2()
およびNAME.get()
を起動するchildTask3()
を実行しているこれらのスレッドによって直接または間接的に実行されるコードは、値"duke
"を読み取ります。
private static final ScopedValue<String> NAME = ScopedValue.newInstance();
ScopedValue.where(NAME, "duke").run(() -> {
try (var scope = new StructuredTaskScope<String>()) {
scope.fork
(() -> childTask1());
scope.fork(() -> childTask2());
scope.fork(() -> childTask3());
scope.join
();
..
}
});
特に指定しないかぎり、このクラスのメソッドにnull
引数を渡すと、NullPointerException
がスローされます。
- APIのノート:
ScopedValue
は、メソッド・パラメータを使用せずにデータの"一方向伝送"が目標である場合、ThreadLocal
より優先される必要があります。ThreadLocal
は、メソッド・パラメータを使用せずにメソッドにデータを渡すために使用できますが、次のような多くの問題が発生します:- 実装上のノート:
- スコープ値は、かなり小さい数値で使用するように設計されています。
get()
は最初にスコープを囲んで検索を実行し、スコープ値の最も内側のバインディングを見つけます。 次に、検索の結果が小さいスレッド・ローカル・キャッシュにキャッシュされます。 そのスコープ値に対するget()
の以降の呼出しは、ほとんどの場合非常に高速です。 ただし、プログラムに周期的に使用するスコープ値が多数ある場合、キャッシュ・ヒット率は低くなり、パフォーマンスは低下します。 この設計により、StructuredTaskScope
PREVIEWスレッドによるスコープ値継承が非常に高速になります: 本質的には、ポインタをコピーし、スコープ値バインディングを残すだけでも、ポインタを更新するよりはほとんど必要ありません。スレッドごとのスコープ値キャッシュは小さいため、クライアントは使用中のバインドされたスコープ値の数を最小限に抑える必要があります。 たとえば、この方法で多数の値を渡す必要がある場合、これらの値を保持するレコード・クラスを作成し、そのレコードのインスタンスに単一の
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 interface
ScopedValue.CallableOpPREVIEW<T, X extends Throwable>
Preview.結果を返し、例外をスローする可能性がある操作。static final class
Preview.「キー」のようなスコープ値の値へのマッピング。 -
メソッドのサマリー
修飾子と型メソッド説明get()
現在のスレッドにバインドされている場合、スコープ値の値を返します。boolean
isBound()
このスコープ値が現在のスレッドにバインドされている場合は、true
を返します。static <T> ScopedValuePREVIEW
<T> すべてのスレッドに対して最初はバインドされていないスコープ値を作成します。現在のスレッドにバインドされている場合、このスコープ値の値を返します。それ以外の場合は、other
を返します。orElseThrow
(Supplier<? extends X> exceptionSupplier) 現在のスレッドにバインドされている場合、このスコープ値の値を返します。それ以外の場合は、例外指定関数によって生成された例外をスローします。static <T> ScopedValue.CarrierPREVIEW
where
(ScopedValuePREVIEW<T> key, T value) ScopedValue
keyから値への単一のマッピングを使用して、新しいCarrier
を作成します。
-
メソッドの詳細
-
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
-
newInstance
public static <T> ScopedValuePREVIEW<T> newInstance()すべてのスレッドに対して最初はバインドされていないスコープ値を作成します。- 型パラメータ:
T
- 値の型- 戻り値:
- 新しい
ScopedValue
-
get
public T get()現在のスレッドにバインドされている場合、スコープ値の値を返します。- 戻り値:
- 現在のスレッドにバインドされている場合のスコープ値の値
- スロー:
NoSuchElementException
- スコープ値がバインドされていない場合
-
isBound
public boolean isBound()このスコープ値が現在のスレッドにバインドされている場合は、true
を返します。- 戻り値:
- このスコープ値が現在のスレッドにバインドされている場合、
true
-
orElse
-
orElseThrow
現在のスレッドにバインドされている場合、このスコープ値の値を返します。それ以外の場合は、例外指定関数によって生成された例外をスローします。- 型パラメータ:
X
- スローされる例外のタイプ- パラメータ:
exceptionSupplier
- スローする例外を生成する供給関数- 戻り値:
- 現在のスレッドにバインドされている場合のスコープ値の値
- スロー:
X
- スコープ値が現在のスレッドにバインドされていない場合
-
ScopedValue
を使用できます。