public class MutableCallSite extends CallSite
MutableCallSite
は、ターゲット変数の動作が通常のフィールドと同じであるような CallSite
です。MutableCallSite
にリンクされた invokedynamic
命令は、すべての呼び出しをそのサイトの現在のターゲットに委譲します。可変コールサイトの動的インボーカも、各呼び出しをそのサイトの現在のターゲットに委譲します。
メソッドハンドルチェーンに状態変数を導入する可変コールサイトの例を、次に示します。
MutableCallSite name = new MutableCallSite(MethodType.methodType(String.class)); MethodHandle MH_name = name.dynamicInvoker(); MethodType MT_str1 = MethodType.methodType(String.class); MethodHandle MH_upcase = MethodHandles.lookup() .findVirtual(String.class, "toUpperCase", MT_str1); MethodHandle worker1 = MethodHandles.filterReturnValue(MH_name, MH_upcase); name.setTarget(MethodHandles.constant(String.class, "Rocky")); assertEquals("ROCKY", (String) worker1.invokeExact()); name.setTarget(MethodHandles.constant(String.class, "Fred")); assertEquals("FRED", (String) worker1.invokeExact()); // (mutation can be continued indefinitely)
同じコールサイトをいくつかの場所で一度に使用できます。
MethodType MT_str2 = MethodType.methodType(String.class, String.class); MethodHandle MH_cat = lookup().findVirtual(String.class, "concat", methodType(String.class, String.class)); MethodHandle MH_dear = MethodHandles.insertArguments(MH_cat, 1, ", dear?"); MethodHandle worker2 = MethodHandles.filterReturnValue(MH_name, MH_dear); assertEquals("Fred, dear?", (String) worker2.invokeExact()); name.setTarget(MethodHandles.constant(String.class, "Wilma")); assertEquals("WILMA", (String) worker1.invokeExact()); assertEquals("Wilma, dear?", (String) worker2.invokeExact());
ターゲット値の非同期: 可変コールサイトのターゲットに書き込んでも、ほかのスレッドがその更新された値を認識するようにはなりません。更新されたコールサイトに関する適切な同期アクションを実行しないスレッドは、古いターゲット値をキャッシュに入れ、新しいターゲット値の使用を無期限に遅らせる可能性があります。(これは、オブジェクトフィールドに適用される Java メモリーモデルの通常の結果です。)
syncAll
操作は、ほかの同期が存在していなくてもスレッドが新しいターゲット値を受け入れるように強制するための手段を提供します。
更新頻度の高いターゲット値では、代わりに揮発性コールサイトの使用を検討してください。
コンストラクタと説明 |
---|
MutableCallSite(MethodHandle target)
初期ターゲットメソッドハンドルを持つコールサイトオブジェクトを作成します。
|
MutableCallSite(MethodType type)
指定されたメソッド型を持つ空のコールサイトオブジェクトを作成します。
|
修飾子と型 | メソッドと説明 |
---|---|
MethodHandle |
dynamicInvoker()
このコールサイトにリンクされている invokedynamic 命令と同等のメソッドハンドルを生成します。
|
MethodHandle |
getTarget()
コールサイトのターゲットメソッドを返しますが、これは、
MutableCallSite の通常のフィールドのように振る舞います。 |
void |
setTarget(MethodHandle newTarget)
このコールサイトのターゲットメソッドを通常の変数として更新します。
|
static void |
syncAll(MutableCallSite[] sites)
指定された配列内のコールサイトごとに同期処理を実行しますが、その際、いずれかのコールサイトのターゲットから以前にロードされたキャッシュ値をすべて破棄することを、ほかのすべてのスレッドに対して強制します。
|
public MutableCallSite(MethodType type)
IllegalStateException
をスローするような、指定された型のメソッドハンドルに設定されます。
コールサイトの型が、指定された型に永続的に設定されます。
この CallSite
オブジェクトがブートストラップメソッドから返されたりほかの何らかの方法で呼び出されたりする前に、通常は setTarget
呼び出し経由でより有用なターゲットメソッドがこのオブジェクトに提供されます。
type
- このコールサイトのメソッド型NullPointerException
- 提案された型が null の場合public MutableCallSite(MethodHandle target)
target
- コールサイトの初期ターゲットとなるメソッドハンドルNullPointerException
- 提案されたターゲットが null の場合public final MethodHandle getTarget()
MutableCallSite
の通常のフィールドのように振る舞います。
getTarget
のメモリーとの相互作用は、通常の変数 (配列の要素や非揮発性の final フィールドなど) から読み取りを行う場合と同じです。
特に、現在のスレッドは、以前のメモリーからのターゲットの読み取り結果を再利用することを選択する可能性があり、別のスレッドによるターゲットへの最新の更新を認識できない可能性があります。
getTarget
、クラス: CallSite
setTarget(java.lang.invoke.MethodHandle)
public void setTarget(MethodHandle newTarget)
メモリーとの相互作用は、通常の変数 (配列の要素や非揮発性の final フィールドなど) への書き込みを行う場合と同じです。
特に、無関係のスレッドは、メモリーからの読み取りを実行するまで、更新されたターゲットを認識できない可能性があります。より確実に保証するには、与えられた任意のコールサイトで使用されるブートストラップメソッドまたはターゲットメソッドあるいはその両方に、適切な処理を組み込みます。
setTarget
、クラス: CallSite
newTarget
- 新しいターゲットNullPointerException
- 提案された新しいターゲットが null の場合WrongMethodTypeException
- 提案された新しいターゲットのメソッド型が以前のターゲットと異なる場合getTarget()
public final MethodHandle dynamicInvoker()
このメソッドは次のコードと同等です。
MethodHandle getTarget, invoker, result; getTarget = MethodHandles.publicLookup().bind(this, "getTarget", MethodType.methodType(MethodHandle.class)); invoker = MethodHandles.exactInvoker(this.type()); result = MethodHandles.foldArguments(invoker, getTarget)
dynamicInvoker
、クラス: CallSite
public static void syncAll(MutableCallSite[] sites)
この処理では、すでに古いターゲット値に対して開始された呼び出しが取り消されることは一切ありません。(Java では未来へのタイムトラベルのみがサポートされます。)
全体の効果としては、各コールサイトのターゲットの将来のすべての読み取り元に、直近で格納された値の受け入れを強制することです。(「直近」は、syncAll
自身を基準にしたものと見なされます。) 逆に言えば、すべての読み取り元が (何らかの方法で) 各コールサイトのターゲットの以前のバージョンをすべてキャッシュから削除するまで、syncAll
の呼び出しがブロックする可能性があります。
setTarget
と syncAll
の呼び出しは一般に、競合状態を回避できるように、何らかの種類の相互排他の下で実行すべきです。読み取り元スレッドは、値をインストールする setTarget
呼び出しと同じくらい早い時期に (値を確認する syncAll
に先だって) ターゲットの更新を認識する可能性があります。一方、読み取り元スレッドが、(更新版を伝えようとする setTarget
のあと) syncAll
呼び出しが返るまで、以前のバージョンのターゲットを認識する可能性もあります。
この処理は、コストが高くなる可能性が高いため、できるかぎり使用しないようにしてください。可能であればそれをバッファーし、一連のコールサイトのバッチ処理を行えるようにしてください。
sites
に null 要素が含まれていると、NullPointerException
が発行されます。この場合、メソッドが異常終了で返る前に、配列内の null でない要素がいくつか処理される可能性があります。それらがどの要素であるか (存在する場合) は、実装に依存します。
個々のコールサイト S
について、明らかな影響を次に示します。
V
の作成と書き込みが行われます。JMM での定義に従い、この書き込みはグローバル同期イベントになります。
V
への揮発性書き込みの前に起こったものとされます。(これは一部の実装では、現在のスレッドがグローバル解放処理を実行することを意味します。)
S
の現在のターゲットへの書き込みは、V
への揮発性書き込みの前に起こったものとされます。
V
への揮発性書き込みは、グローバル同期順序で (実装固有の方法で) 配置されます。
T
(現在のスレッド以外) を考えます。T
は、(グローバル同期順序で) V
への揮発性書き込みのあとで同期アクション A
を実行した場合、その結果、S
の現在のターゲット、または S
のターゲットに対する読み取りを実行した場合はそのターゲットへの後続の書き込み、のどちらかを認識する必要があります。(この制約は「同期順序の一貫性」と呼ばれます。)
V
は除去されません (書き込まれた値が不確定で読み取られた値が使用されない場合でも)。
T
がアクション A
の直後に V
の揮発性読み取りを実行したかのようになります。この読み取りは、T
のローカルアクション順序で、S
のターゲットの将来のあらゆる読み取りの前に起こります。それはまるで、実装が、T
による S
のターゲットの読み取りを任意に選択し、それに先だって V
の読み取りを強制することで、新しいターゲット値の伝達を保証したかのようになります。
実装は Java メモリーモデルの制約に従うかぎり、ほかのスレッド (上では T
) が S
のターゲットの以前の値を使用し続けている間、syncAll
の処理の完了を遅らせてもかまいません。ただし、実装では (いつものように)、ライブロックを避け、更新されたターゲットを考慮することを最終的にはすべてのスレッドに要求することをお勧めします。
解説: syncAll
はパフォーマンス上の理由により、単一のコールサイト上の仮想メソッドではなく、一連のコールサイトに適用されます。一部の実装では、1 つ以上の同期処理のために固定の大きなオーバーヘッドコストが発生する一方で、コールサイトを 1 つ追加するごとに増えるコストが小さい可能性があります。いずれにせよ、ほかのスレッドに何らかの方法で割り込みを行なって更新されたターゲット値を知らせなければいけない可能性があるため、この処理はおそらく高コストになります。ただし、いくつかのコールサイトを同期させる単一の呼び出しは、いずれかのサイトだけに対する呼び出しを複数回行うのと形式上は同じ効果がある、ということは言えます。
実装上の注意: MutableCallSite
の単純な実装では、可変コールサイトのターゲットとして揮発性変数が使用される可能性があります。そのような実装では syncAll
メソッドが無操作になる可能性がありますが、それは、前述の JMM 動作に準拠しています。
sites
- 同期するコールサイトの配列NullPointerException
- sites
配列参照が null であるか、配列内に null が含まれている場合 バグまたは機能を送信
詳細な API リファレンスおよび開発者ドキュメントについては、Java SE のドキュメントを参照してください。そのドキュメントには、概念的な概要、用語の定義、回避方法、有効なコード例などの、開発者を対象にしたより詳細な説明が含まれています。
Copyright © 1993, 2013, Oracle and/or its affiliates. All rights reserved.