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]));
}
}
- 導入されたバージョン:
- 1.7
-
コンストラクタのサマリー
-
メソッドのサマリー
修飾子と型メソッド説明guardWithTest
(MethodHandle target, MethodHandle fallback) 常にターゲット、フォール・バックのいずれかに委譲するメソッド・ハンドルを返します。boolean
このスイッチ・ポイントが無効化されているかどうかを判定します。static void
invalidateAll
(SwitchPoint[] switchPoints) 指定されたすべてのスイッチ・ポイントを無効状態に設定します。
-
コンストラクタの詳細
-
SwitchPoint
public SwitchPoint()新しいスイッチ・ポイントを作成します。
-
-
メソッドの詳細
-
hasBeenInvalidated
public boolean hasBeenInvalidated()このスイッチ・ポイントが無効化されているかどうかを判定します。解説: 無効化の一方向の性質のため、スイッチ・ポイントがいったん
hasBeenInvalidated
に対してtrueを返し始めると、以降も常にそうなります。 一方、ほかのスレッドから可視の有効なスイッチ・ポイントも、別のスレッドからの要求のためにいつでも無効にされる可能性があります。無効化はグローバルかつ即時的な処理なので、有効なスイッチ・ポイントでのこのクエリーの実行は、内部的には、無効化の原因となる可能性のあるその他のすべてのスレッドとともに順序付けされる必要があります。 したがって、このクエリーのコストは高くなる可能性があります。 スイッチ・ポイント
s
の無効化の状態を問い合わせるブール値メソッド・ハンドルを構築するための推奨方法は、trueおよびfalseの定数
メソッド・ハンドルでs.guardWithTest
を呼び出すことです。- 戻り値:
- このスイッチ・ポイントが無効にされている場合はtrue
-
guardWithTest
public MethodHandle guardWithTest(MethodHandle target, MethodHandle fallback) 常にターゲット、フォール・バックのいずれかに委譲するメソッド・ハンドルを返します。 このメソッド・ハンドルは、スイッチ・ポイントが有効な間だけターゲットに委譲します。 その後、これは永続的にフォール・バックに委譲します。ターゲットとフォール・バックのメソッド型は厳密に同じである必要があり、結果となる複合メソッド・ハンドルの型もこの型になります。
- パラメータ:
target
- 有効期間中のスイッチ・ポイントによって選択されるメソッド・ハンドルfallback
- 無効化後のスイッチ・ポイントによって選択されるメソッド・ハンドル- 戻り値:
- 常にターゲットまたはフォール・バックのいずれかを呼び出す複合メソッド・ハンドル
- 例外:
NullPointerException
- どちらかの引数がnullの場合IllegalArgumentException
- 2つのメソッド型が一致しない場合- 関連項目:
-
invalidateAll
public static void invalidateAll(SwitchPoint[] switchPoints) 指定されたすべてのスイッチ・ポイントを無効状態に設定します。 この呼出しの実行後は、すべてのスレッドで、どのスイッチ・ポイントも有効状態と認識されなくなります。この処理は、コストが高くなる可能性が高いため、できるかぎり使用しないようにしてください。 可能であればそのバッファを作成し、一連のスイッチ・ポイントのバッチ処理を行えるようにしてください。
switchPoints
にnull要素が含まれていると、NullPointerException
が発行されます。 この場合、メソッドが異常終了で返る前に、配列内のnullでない要素がいくつか処理される可能性があります。 それらがどの要素であるか(存在する場合)は、実装に依存します。解説:
invalidateAll
はパフォーマンス上の理由により、単一のスイッチ・ポイント上の仮想メソッドにはなっておらず、一連のスイッチ・ポイントに適用されるようになっています。 一部の実装では、1つ以上の無効化処理のために固定の大きなオーバーヘッド・コストが発生する一方で、無効化を1つ追加するごとに増えるコストは小さい可能性があります。 いずれにせよ、ほかのスレッドに何らかの方法で割込みを行って更新されたスイッチ・ポイントの状態を知らせなければいけない可能性があるため、この処理はおそらく高コストになります。 ただし、いくつかのスイッチ・ポイントを無効化する単一の呼出しは、いずれかのスイッチ・ポイントだけに対する呼出しを複数回行うのと形式上は同じ効果がある、ということは言えます。実装上のノート:
SwitchPoint
の単純な実装では、プライベートのMutableCallSite
を使ってスイッチ・ポイントの状態が発行される可能性があります。 そのような実装では、invalidateAll
メソッドは単純に、コール・サイトのターゲットを変更し、すべてのプライベート・コール・サイトを同期させる呼出しを1回発行することができます。- パラメータ:
switchPoints
- 同期するコール・サイトの配列- 例外:
NullPointerException
-switchPoints
配列参照がnullであるか、配列内にnullが含まれている場合
-