- 型パラメータ:
T
- このScopedValue
にバインドされたオブジェクトのタイプ
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
-
ネストされたクラスのサマリー
-
メソッドのサマリー
修飾子と型メソッド説明get()
現在のスレッドにバインドされている場合、スコープ値の値を返します。boolean
isBound()
このスコープ値が現在のスレッドにバインドされている場合は、true
を返します。static <T> ScopedValue<T>
すべてのスレッドに対して最初はバインドされていないスコープ値を作成します。現在のスレッドにバインドされている場合、このスコープ値の値を返します。それ以外の場合は、other
を返します。orElseThrow
(Supplier<? extends X> exceptionSupplier) 現在のスレッドにバインドされている場合、このスコープ値の値を返します。それ以外の場合は、例外指定関数によって生成された例外をスローします。static <T> ScopedValue.Carrier
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
を使用して、値を返す操作をコールします。
-
メソッドの詳細
-
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
がスローされます。 -
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
現在のスレッドにバインドされている場合、このスコープ値の値を返します。それ以外の場合は、other
を返します。- パラメータ:
other
- バインドされていない場合に返される値は、null
です- 戻り値:
- バインドされている場合はスコープ値、そうでない場合は
other
-
orElseThrow
現在のスレッドにバインドされている場合、このスコープ値の値を返します。それ以外の場合は、例外指定関数によって生成された例外をスローします。- 型パラメータ:
X
- スローされる可能性がある例外のタイプ- パラメータ:
exceptionSupplier
- スローする例外を生成する供給関数- 戻り値:
- 現在のスレッドにバインドされている場合のスコープ値の値
- 例外:
X
- スコープ値が現在のスレッドにバインドされていない場合
-