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, 2014, Oracle and/or its affiliates. All rights reserved.