モジュール java.base
パッケージ java.lang.invoke

クラスSwitchPoint


  • public class SwitchPoint
    extends Object

    SwitchPointは、状態遷移をほかのスレッドに発行できるオブジェクトです。 スイッチ・ポイントは、最初は有効状態になっていますが、いつでも無効状態に変更される可能性があります。 無効化を取り消すことはできません。 スイッチ・ポイントは、保護対象ペアのメソッド・ハンドルを組み合わせて1つの保護デリゲータを作成できます。 保護デリゲータとは、古いメソッド・ハンドルのいずれかに委譲するようなメソッド・ハンドルのことです。 スイッチ・ポイントの状態によって、2つのどちらに委譲されるかが決まります。

    単一のスイッチ・ポイントを使って任意の数のメソッド・ハンドルを制御できます。 (したがって、これは間接的に任意の数のコール・サイトを制御できます。) これを行うには、任意の数の保護対象メソッド・ハンドル・ペアを組み合わせて保護デリゲータを作成するためのファクトリとして、単一のスイッチ・ポイントを使用します。

    保護対象ペアから保護デリゲータが作成されるときに、それらのペアは新しいメソッド・ハンドルM内にラップされますが、新しいメソッド・ハンドルは、その作成元であるスイッチ・ポイントに永続的に関連付けられます。 各ペアはターゲットTとフォール・バックFで構成されます。 スイッチ・ポイントが有効な間は、Mの呼出しがTに委譲されます。 それが無効化されたあとは、呼出しがFに委譲されます。

    Mが呼び出されるたびに参照される揮発性のboolean変数がスイッチ・ポイントに含まれているかのように、無効化はグローバルかつ即時的なものになります。 無効化は永続的でもありますが、これは、スイッチ・ポイントの状態を変更できるのは1回だけであることを意味します。 無効化されたあとのスイッチ・ポイントは、常にFに委譲します。 その時点で、guardWithTestTを無視し、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
    • コンストラクタの詳細

      • 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つのメソッド型が一致しない場合
        関連項目:
        MethodHandles.guardWithTest(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle)
      • invalidateAll

        public static void invalidateAll​(SwitchPoint[] switchPoints)
        指定されたすべてのスイッチ・ポイントを無効状態に設定します。 この呼出しの実行後は、すべてのスレッドで、どのスイッチ・ポイントも有効状態と認識されなくなります。

        この処理は、コストが高くなる可能性が高いため、できるかぎり使用しないようにしてください。 可能であればそのバッファを作成し、一連のスイッチ・ポイントのバッチ処理を行えるようにしてください。

        switchPointsにnull要素が含まれていると、NullPointerExceptionが発行されます。 この場合、メソッドが異常終了で返る前に、配列内のnullでない要素がいくつか処理される可能性があります。 それらがどの要素であるか(存在する場合)は、実装に依存します。

        解説: invalidateAllはパフォーマンス上の理由により、単一のスイッチ・ポイント上の仮想メソッドにはなっておらず、一連のスイッチ・ポイントに適用されるようになっています。 一部の実装では、1つ以上の無効化処理のために固定の大きなオーバーヘッド・コストが発生する一方で、無効化を1つ追加するごとに増えるコストは小さい可能性があります。 いずれにせよ、ほかのスレッドに何らかの方法で割込みを行って更新されたスイッチ・ポイントの状態を知らせなければいけない可能性があるため、この処理はおそらく高コストになります。 ただし、いくつかのスイッチ・ポイントを無効化する単一の呼出しは、いずれかのスイッチ・ポイントだけに対する呼出しを複数回行うのと形式上は同じ効果がある、ということは言えます。

        実装上の注意: SwitchPointの単純な実装では、プライベートのMutableCallSiteを使ってスイッチ・ポイントの状態が発行される可能性があります。 そのような実装では、invalidateAllメソッドは単純に、コール・サイトのターゲットを変更し、すべてのプライベート・コール・サイトを同期させる呼出しを1回発行することができます。

        パラメータ:
        switchPoints - 同期するコール・サイトの配列
        例外:
        NullPointerException - switchPoints配列参照がnullであるか、配列内にnullが含まれている場合