- 直系の既知のサブクラス:
AbstractRelinkableCallSite
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
操作は、ほかの同期が存在していなくてもスレッドが新しいターゲット値を受け入れるように強制するための手段を提供します。
更新頻度の高いターゲット値では、代わりに揮発性コール・サイトの使用を検討してください。
- 導入されたバージョン:
- 1.7
-
コンストラクタのサマリー
コンストラクタ説明MutableCallSite
(MethodHandle target) 初期ターゲット・メソッド・ハンドルを持つコール・サイト・オブジェクトを作成します。MutableCallSite
(MethodType type) 指定されたメソッド型を持つ空のコール・サイト・オブジェクトを作成します。 -
メソッドのサマリー
修飾子と型メソッド説明final MethodHandle
このコール・サイトにリンクされているinvokedynamic命令と同等のメソッド・ハンドルを生成します。final MethodHandle
コール・サイトのターゲット・メソッドを返しますが、これは、MutableCallSite
の通常のフィールドのように振る舞います。void
setTarget
(MethodHandle newTarget) このコール・サイトのターゲット・メソッドを通常の変数として更新します。static void
syncAll
(MutableCallSite[] sites) 指定された配列内のコール・サイトごとに同期処理を実行しますが、その際、いずれかのコール・サイトのターゲットから以前にロードされたキャッシュ値をすべて破棄することを、ほかのすべてのスレッドに対して強制します。
-
コンストラクタの詳細
-
MutableCallSite
public MutableCallSite(MethodType type) 指定されたメソッド型を持つ空のコール・サイト・オブジェクトを作成します。 初期ターゲットは、呼出し時にIllegalStateException
をスローするような、指定された型のメソッド・ハンドルに設定されます。コール・サイトの型が、指定された型に永続的に設定されます。
この
CallSite
オブジェクトがブートストラップ・メソッドから返されたりほかの何らかの方法で呼び出されたりする前に、通常はsetTarget
呼出し経由でより有用なターゲット・メソッドがこのオブジェクトに提供されます。- パラメータ:
type
- このコール・サイトのメソッド型- 例外:
NullPointerException
- 提案された型がnullの場合
-
MutableCallSite
public MutableCallSite(MethodHandle target) 初期ターゲット・メソッド・ハンドルを持つコール・サイト・オブジェクトを作成します。 コール・サイトの型が初期ターゲットの型に永続的に設定されます。- パラメータ:
target
- コール・サイトの初期ターゲットとなるメソッド・ハンドル- 例外:
NullPointerException
- 提案されたターゲットがnullの場合
-
-
メソッドの詳細
-
getTarget
public final MethodHandle getTarget()コール・サイトのターゲット・メソッドを返しますが、これは、MutableCallSite
の通常のフィールドのように振る舞います。getTarget
のメモリーとの相互作用は、通常の変数(配列の要素や非揮発性のfinalフィールドなど)から読取りを行う場合と同じです。特に、現在のスレッドは、以前のメモリーからのターゲットの読取り結果を再利用することを選択する可能性があり、別のスレッドによるターゲットへの最新の更新を認識できない可能性があります。
-
setTarget
public void setTarget(MethodHandle newTarget) このコール・サイトのターゲット・メソッドを通常の変数として更新します。 新しいターゲットの型は古いターゲットの型と同じでなければいけません。メモリーとの相互作用は、通常の変数(配列の要素や非揮発性のfinalフィールドなど)への書込みを行う場合と同じです。
特に、無関係のスレッドは、メモリーからの読取りを実行するまで、更新されたターゲットを認識できない可能性があります。 より確実に保証するには、与えられた任意のコール・サイトで使用されるブートストラップ・メソッドまたはターゲット・メソッドあるいはその両方に、適切な処理を組み込みます。
- 定義:
setTarget
、クラスCallSite
- パラメータ:
newTarget
- 新しいターゲット- 例外:
NullPointerException
- 提案された新しいターゲットがnullの場合WrongMethodTypeException
- 提案された新しいターゲットのメソッド型が以前のターゲットと異なる場合- 関連項目:
-
dynamicInvoker
public final MethodHandle dynamicInvoker()このコール・サイトにリンクされているinvokedynamic命令と同等のメソッド・ハンドルを生成します。このメソッドは次のコードと同等です。
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
- 戻り値:
- このコール・サイトの現在のターゲットを常に呼び出すメソッド・ハンドル
-
syncAll
public static void syncAll(MutableCallSite[] sites) 指定された配列内のコール・サイトごとに同期処理を実行しますが、その際、いずれかのコール・サイトのターゲットから以前にロードされたキャッシュ値をすべて破棄することを、ほかのすべてのスレッドに対して強制します。この処理では、すでに古いターゲット値に対して開始された呼出しが取り消されることは一切ありません。 (Javaでは未来へのタイム・トラベルのみがサポートされます。)
全体の効果としては、各コール・サイトのターゲットの将来のすべての読取り元に、直近で格納された値の受け入れを強制することです。 (「直近」は、
syncAll
自身を基準にしたものと見なされます。) 逆に言えば、すべての読取り元が(何らかの方法で)各コール・サイトのターゲットの以前のバージョンをすべてキャッシュから削除するまで、syncAll
の呼出しがブロックする可能性があります。setTarget
とsyncAll
の呼出しは一般に、競合状態を回避できるように、何らかの種類の相互排他の下で実行すべきです。 読取り元スレッドは、値をインストールするsetTarget
呼び出しと同じくらい早い時期に(値を確認するsyncAll
に先だって)ターゲットの更新を認識する可能性があります。 一方、読取り元スレッドが、(更新版を伝えようとするsetTarget
のあと)syncAll
呼出しが返るまで、以前のバージョンのターゲットを認識する可能性もあります。この処理は、コストが高くなる可能性が高いため、できるかぎり使用しないようにしてください。 可能であればそれをバッファし、一連のコール・サイトのバッチ処理を行えるようにしてください。
sites
にnull要素が含まれていると、NullPointerException
が発行されます。 この場合、メソッドが異常終了で返る前に、配列内のnullでない要素がいくつか処理される可能性があります。 それらがどの要素であるか(存在する場合)は、実装に依存します。Javaメモリー・モデルの詳細
Javaメモリー・モデルの観点では、この処理で実行される同期アクションは効果の点で、現在のスレッドによる揮発性変数への書き込み、および影響を受けるコール・サイトのいずれかにアクセスする可能性のあるその他のすべてのスレッドによる最終的な揮発性読取りと同等になります。個々のコール・サイト
S
について、明らかな影響を次に示します。- 現在のスレッドによって新しい揮発性変数
V
の作成と書込みが行われます。 JMMでの定義に従い、この書込みはグローバル同期イベントになります。 - 書込みイベントのスレッド・ローカル順序では当然のことですが、現在のスレッドがすでに実行したアクションはすべて、
V
への揮発性書込みの前に起こったものとされます。 (これは一部の実装では、現在のスレッドがグローバル解放処理を実行することを意味します。) - 具体的には、
S
の現在のターゲットへの書込みは、V
への揮発性書込みの前に起こったものとされます。 V
への揮発性書込みは、グローバル同期順序で(実装固有の方法で)配置されます。- 任意のスレッド
T
(現在のスレッド以外)を考えます。T
は、(グローバル同期順序で)V
への揮発性書込みのあとで同期アクションA
を実行した場合、その結果、S
の現在のターゲット、またはS
のターゲットに対する読取りを実行した場合はそのターゲットへの後続の書き込み、のどちらかを認識する必要があります。 (この制約は「同期順序の一貫性」と呼ばれます。) - JMMでは特に、無益であることがわかっている変数の読み取りや書込みを除去するようにコンパイラを最適化できます。 そのような除去される読み取りや書込みは、「happens-before」関係に対して何の効果も持ちません。 この事実にもかかわらず、揮発性の
V
は除去されません(書き込まれた値が不確定で読み取られた値が使用されない場合でも)。
T
がアクションA
の直後にV
の揮発性読取りを実行したかのようになります。 この読取りは、T
のローカル・アクション順序で、S
のターゲットの将来のあらゆる読取りの前に起こります。 それはまるで、実装が、T
によるS
のターゲットの読取りを任意に選択し、それに先だってV
の読取りを強制することで、新しいターゲット値の伝達を保証したかのようになります。実装はJavaメモリー・モデルの制約に従うかぎり、ほかのスレッド(上では
T
)がS
のターゲットの以前の値を使用し続けている間、syncAll
の処理の完了を遅らせてもかまいません。 ただし、実装では(いつものように)、ライブロックを避け、更新されたターゲットを考慮することを最終的にはすべてのスレッドに要求することをお薦めします。解説:
syncAll
はパフォーマンス上の理由により、単一のコール・サイト上の仮想メソッドではなく、一連のコール・サイトに適用されます。 一部の実装では、1つ以上の同期処理のために固定の大きなオーバーヘッド・コストが発生する一方で、コール・サイトを1つ追加するごとに増えるコストが小さい可能性があります。 いずれにせよ、ほかのスレッドに何らかの方法で割込みを行って更新されたターゲット値を知らせなければいけない可能性があるため、この処理はおそらく高コストになります。 ただし、いくつかのコール・サイトを同期させる単一の呼出しは、いずれかのサイトだけに対する呼出しを複数回行うのと形式上は同じ効果がある、ということは言えます。実装上のノート:
MutableCallSite
の単純な実装では、可変コール・サイトのターゲットとして揮発性変数が使用される可能性があります。 そのような実装ではsyncAll
メソッドが無操作になる可能性がありますが、それは、前述のJMM動作に準拠しています。- パラメータ:
sites
- 同期するコール・サイトの配列- 例外:
NullPointerException
-sites
配列参照がnullであるか、配列内にnullが含まれている場合
- 現在のスレッドによって新しい揮発性変数
-