- 型パラメータ:
T
- 値の型
ScopedValue
は、JavaプラットフォームのプレビューAPIです。
Javaプログラミング言語では、データは通常、メソッド・パラメータを使用してメソッドに渡されます。 データを使用するメソッドを取得するには、様々なメソッドのシーケンスでデータを渡す必要があります。 呼出しのシーケンス内のすべてのメソッドがパラメータを宣言する必要があり、すべてのメソッドがデータにアクセスできます。 ScopedValue
は、メソッド・パラメータを使用せずに遠隔地メソッド(通常、「コールバック」)にデータを渡す手段を提供します。 実際には、ScopedValue
は「暗黙的なメソッド・パラメータ」です。 "かのように"は、一連の呼出しのすべてのメソッドに追加パラメータを持ちます。 どのメソッドもパラメータを宣言せず、ScopedValue
オブジェクトにアクセスできるメソッドのみがその値(データ)にアクセスできます。 ScopedValue
を使用すると、データのパラメータを宣言せず、データにアクセスできない一連の中間メソッドを介して、「発信者」から遠隔地「逃す」にデータを安全に渡すことができます。
ScopedValue
APIは、ScopedValue
オブジェクトboundを持つメソッドを、メソッドの限界実行期間の値に対して実行することで機能します。 このメソッドは別のメソッドを呼び出すことができ、別のメソッドを呼び出すことができます。 メソッドの展開実行では、「動的スコープ」を定義します。 ScopedValue
オブジェクトにアクセスできるこれらのメソッドのコードは、その値を読み取ることができます。 元のメソッドが正常に完了した場合、または例外が発生した場合、ScopedValue
オブジェクトは「バインドなし」に戻ります。 ScopedValue
APIでは、値にバインドされたScopedValue
を使用して、Runnable.run
、Callable.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
は、スレッド間での共有をサポートします。 この共有は、親スレッドによる制限された実行期間内に子スレッドが開始および終了する構造化されたケースに制限されます。 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.runWhere(NAME, "duke", () -> {
try (var scope = new StructuredTaskScope<String>()) {
scope.fork(() -> childTask1());
scope.fork(() -> childTask2());
scope.fork(() -> childTask3());
...
}
});
特に指定しないかぎり、このクラスのメソッドに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 <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
isBound()
このスコープ値が現在のスレッドにバインドされている場合は、true
を返します。static <T> ScopedValuePREVIEW
<T> すべてのスレッドに対して最初はバインドされていないスコープ値を作成します。現在のスレッドにバインドされている場合、このスコープ値の値を返します。それ以外の場合は、other
を返します。orElseThrow
(Supplier<? extends X> exceptionSupplier) 現在のスレッドにバインドされている場合、このスコープ値の値を返します。それ以外の場合は、例外指定関数によって生成された例外をスローします。static <T> void
runWhere
(ScopedValuePREVIEW<T> key, T value, Runnable op) 現在のスレッドの値にバインドされたScopedValue
を使用して操作を実行します。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
-
callWhere
public static <T,R> R callWhere(ScopedValuePREVIEW<T> key, T value, Callable<? extends R> op) throws Exception 現在のスレッドの値にバインドされたScopedValue
を使用して、値を返す操作をコールします。 操作が(通常または例外あり)を完了すると、ScopedValue
はバインドされていない状態に戻されるか、現在のスレッドで以前バインドされていた場合は以前の値に戻されます。op
が例外ありで完了すると、このメソッドによって伝播されます。スコープ値は、「構造化方式」で使用することを目的としています。 操作によって直接または間接的に呼び出されたコードで
StructuredTaskScope
PREVIEWが作成するが、それを「閉じ」PREVIEWない場合、操作が完了すると(通常または例外あり) 「構造違反」として検出されます。 その場合、StructuredTaskScope
の基礎となる構造が閉じられ、StructureViolationException
PREVIEWがスローされます。- 実装上のノート:
- このメソッドは、次のように実装されます:
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
が例外ありで完了すると、このメソッドによって伝播されます。スコープ値は、「構造化方式」で使用することを目的としています。 操作によって直接または間接的に呼び出されたコードで
StructuredTaskScope
PREVIEWが作成するが、それを「閉じ」PREVIEWない場合、操作が完了すると(通常または例外あり) 「構造違反」として検出されます。 その場合、StructuredTaskScope
の基礎となる構造が閉じられ、StructureViolationException
PREVIEWがスローされます。- 実装上のノート:
- このメソッドは、次のように実装されます:
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
が例外ありで完了すると、このメソッドによって伝播されます。スコープ値は、「構造化方式」で使用することを目的としています。 操作によって直接または間接的に呼び出されたコードで
StructuredTaskScope
PREVIEWが作成するが、それを「閉じ」PREVIEWない場合、操作が完了すると(通常または例外あり) 「構造違反」として検出されます。 その場合、StructuredTaskScope
の基礎となる構造が閉じられ、StructureViolationException
PREVIEWがスローされます。- 実装上のノート:
- このメソッドは、次のように実装されます:
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
-
orElseThrow
現在のスレッドにバインドされている場合、このスコープ値の値を返します。それ以外の場合は、例外指定関数によって生成された例外をスローします。- 型パラメータ:
X
- スローされる可能性がある例外のタイプ- パラメータ:
exceptionSupplier
- スローする例外を生成する供給関数- 戻り値:
- 現在のスレッドにバインドされている場合のスコープ値の値
- 例外:
X
- スコープ値が現在のスレッドにバインドされていない場合
-
ScopedValue
を使用できます。