public class SwitchPoint extends Object
SwitchPoint
は、状態遷移をほかのスレッドに発行できるオブジェクトです。スイッチポイントは、最初は有効状態になっていますが、いつでも無効状態に変更される可能性があります。無効化を取り消すことはできません。スイッチポイントは、保護対象ペアのメソッドハンドルを組み合わせて 1 つの保護デリゲータを作成できます。保護デリゲータとは、古いメソッドハンドルのいずれかに委譲するようなメソッドハンドルのことです。スイッチポイントの状態によって、2 つのどちらに委譲されるかが決まります。
単一のスイッチポイントを使って任意の数のメソッドハンドルを制御できます。(したがって、これは間接的に任意の数のコールサイトを制御できます。)これを行うには、任意の数の保護対象メソッドハンドルペアを組み合わせて保護デリゲータを作成するためのファクトリとして、単一のスイッチポイントを使用します。
保護対象ペアから保護デリゲータが作成されるときに、それらのペアは新しいメソッドハンドル M
内にラップされますが、新しいメソッドハンドルは、その作成元であるスイッチポイントに永続的に関連付けられます。各ペアはターゲット T
とフォールバック F
で構成されます。スイッチポイントが有効な間は、M
の呼び出しが T
に委譲されます。それが無効化されたあとは、呼び出しが F
に委譲されます。
M
が呼び出されるたびに参照される揮発性の boolean 変数がスイッチポイントに含まれているかのように、無効化はグローバルかつ即時的なものになります。無効化は永続的でもありますが、これは、スイッチポイントの状態を変更できるのは 1 回だけであることを意味します。無効化されたあとのスイッチポイントは、常に F
に委譲します。その時点で、guardWithTest
は T
を無視し、F
を返す可能性があります。
実際のスイッチポイントの例を、次に示します。
MethodHandle MH_strcat = MethodHandles.lookup() .findVirtual(String.class, "concat", MethodType.methodType(String.class, String.class)); SwitchPoint spt = new SwitchPoint(); assert(!spt.hasBeenInvalidated()); // the following steps may be repeated to re-use the same switch point: MethodHandle worker1 = MH_strcat; MethodHandle worker2 = MethodHandles.permuteArguments(MH_strcat, MH_strcat.type(), 1, 0); MethodHandle worker = spt.guardWithTest(worker1, worker2); assertEquals("method", (String) worker.invokeExact("met", "hod")); SwitchPoint.invalidateAll(new SwitchPoint[]{ spt }); assert(spt.hasBeenInvalidated()); assertEquals("hodmet", (String) worker.invokeExact("met", "hod"));
解説: スイッチポイントはサブクラス化しなくても有用です。これらはサブクラス化することもできます。これは、アプリケーション固有の無効化ロジックをスイッチポイントに関連付ける場合に役立つ可能性があります。スイッチポイントと、それが生成および消費するメソッドハンドルとの間には、永続的な関係は一切存在しません。ガベージコレクタは、スイッチポイントによって生成または消費されたメソッドハンドルを、スイッチポイント自体の存続期間とは無関係に収集する可能性があります。
実装上の注意: スイッチポイントは、MutableCallSite
に基づいておおよそ次のように実装されているかのように動作します。
public class SwitchPoint { private static final MethodHandle K_true = MethodHandles.constant(boolean.class, true), K_false = MethodHandles.constant(boolean.class, false); private final MutableCallSite mcs; private final MethodHandle mcsInvoker; public SwitchPoint() { this.mcs = new MutableCallSite(K_true); this.mcsInvoker = mcs.dynamicInvoker(); } public MethodHandle guardWithTest( MethodHandle target, MethodHandle fallback) { // Note: mcsInvoker is of type ()boolean. // Target and fallback may take any arguments, but must have the same type. return MethodHandles.guardWithTest(this.mcsInvoker, target, fallback); } public static void invalidateAll(SwitchPoint[] spts) { List<MutableCallSite> mcss = new ArrayList<>(); for (SwitchPoint spt : spts) mcss.add(spt.mcs); for (MutableCallSite mcs : mcss) mcs.setTarget(K_false); MutableCallSite.syncAll(mcss.toArray(new MutableCallSite[0])); } }
コンストラクタと説明 |
---|
SwitchPoint()
新しいスイッチポイントを作成します。
|
修飾子と型 | メソッドと説明 |
---|---|
MethodHandle |
guardWithTest(MethodHandle target, MethodHandle fallback)
常にターゲット、フォールバックのいずれかに委譲するメソッドハンドルを返します。
|
boolean |
hasBeenInvalidated()
このスイッチポイントが無効化されているかどうかを判定します。
|
static void |
invalidateAll(SwitchPoint[] switchPoints)
指定されたすべてのスイッチポイントを無効状態に設定します。
|
public boolean hasBeenInvalidated()
解説: 無効化の一方向の性質のため、スイッチポイントがいったん hasBeenInvalidated
に対して true を返し始めると、以降も常にそうなります。一方、ほかのスレッドから可視の有効なスイッチポイントも、別のスレッドからの要求のためにいつでも無効にされる可能性があります。
無効化はグローバルかつ即時的な処理なので、有効なスイッチポイントでのこのクエリーの実行は、内部的には、無効化の原因となる可能性のあるその他のすべてのスレッドとともに順序付けされる必要があります。したがって、このクエリーのコストは高くなる可能性があります。スイッチポイント s
の無効化の状態を問い合わせるブール値メソッドハンドルを構築するための推奨方法は、true および false の定数
メソッドハンドルで s.guardWithTest
を呼び出すことです。
public MethodHandle guardWithTest(MethodHandle target, MethodHandle fallback)
ターゲットとフォールバックのメソッド型は厳密に同じである必要があり、結果となる複合メソッドハンドルの型もこの型になります。
target
- 有効期間中のスイッチポイントによって選択されるメソッドハンドルfallback
- 無効化後のスイッチポイントによって選択されるメソッドハンドルNullPointerException
- どちらかの引数が null の場合IllegalArgumentException
- 2 つのメソッド型が一致しない場合MethodHandles.guardWithTest(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle)
public static void invalidateAll(SwitchPoint[] switchPoints)
この処理は、コストが高くなる可能性が高いため、できるかぎり使用しないようにしてください。可能であればそのバッファーを作成し、一連のスイッチポイントのバッチ処理を行えるようにしてください。
switchPoints
に null 要素が含まれていると、NullPointerException
が発行されます。この場合、メソッドが異常終了で返る前に、配列内の null でない要素がいくつか処理される可能性があります。それらがどの要素であるか (存在する場合) は、実装に依存します。
解説: invalidateAll
はパフォーマンス上の理由により、単一のスイッチポイント上の仮想メソッドにはなっておらず、一連のスイッチポイントに適用されるようになっています。一部の実装では、1 つ以上の無効化処理のために固定の大きなオーバーヘッドコストが発生する一方で、無効化を 1 つ追加するごとに増えるコストは小さい可能性があります。いずれにせよ、ほかのスレッドに何らかの方法で割り込みを行なって更新されたスイッチポイントの状態を知らせなければいけない可能性があるため、この処理はおそらく高コストになります。ただし、いくつかのスイッチポイントを無効化する単一の呼び出しは、いずれかのスイッチポイントだけに対する呼び出しを複数回行うのと形式上は同じ効果がある、ということは言えます。
実装上の注意: SwitchPoint
の単純な実装では、プライベートの MutableCallSite
を使ってスイッチポイントの状態が発行される可能性があります。そのような実装では、invalidateAll
メソッドは単純に、コールサイトのターゲットを変更し、すべてのプライベートコールサイトを同期させる呼び出しを 1 回発行することができます。
switchPoints
- 同期するコールサイトの配列NullPointerException
- switchPoints
配列参照が null であるか、配列内に null が含まれている場合 バグまたは機能を送信
詳細な API リファレンスおよび開発者ドキュメントについては、Java SE のドキュメントを参照してください。そのドキュメントには、概念的な概要、用語の定義、回避方法、有効なコード例などの、開発者を対象にしたより詳細な説明が含まれています。
Copyright © 1993, 2013, Oracle and/or its affiliates. All rights reserved.