SwitchPoint
是一个对象,可以向其他线程发布状态转换。开关点最初处于有效状态,但随时可以更改为无效状态。无法撤消失效。开关点可以将一对受保护的方法句柄组合成受保护的委托者。受保护的委托者是一个方法句柄,它委托给旧的方法句柄中的一个。开关点的状态决定了委托给哪一个。
单个开关点可以用于控制任意数量的方法句柄。(间接地,因此它可以控制任意数量的调用点。)这是通过使用单个开关点作为将任意数量的受保护方法句柄对组合成受保护委托者的工厂来实现的。
当从受保护的一对中创建受保护的委托者时,该对被包装在一个新的方法句柄M
中,该方法句柄永久与创建它的开关点关联。每对包括一个目标T
和一个回退F
。在开关点有效时,对M
的调用被委托给T
。在失效后,调用被委托给F
。
失效是全局和立即的,就好像开关点包含一个在每次调用M
时都要查询的易失性布尔变量一样。失效也是永久的,这意味着开关点只能改变状态一次。开关点在失效后将始终委托给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());
// 以下步骤可以重复使用相同的开关点:
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) {
// 注意:mcsInvoker的类型为()boolean。
// 目标和回退可以接受任何参数,但必须具有相同的类型。
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]));
}
}
- 自 JDK 版本:
- 1.7
-
Constructor Summary
-
Method Summary
Modifier and TypeMethodDescriptionguardWithTest
(MethodHandle target, MethodHandle fallback) 返回一个方法句柄,该方法句柄始终委托给目标或回退。boolean
确定此开关点是否已失效。static void
invalidateAll
(SwitchPoint[] switchPoints) 将所有给定的开关点设置为无效状态。
-
Constructor Details
-
SwitchPoint
public SwitchPoint()创建一个新的开关点。
-
-
Method Details
-
hasBeenInvalidated
public boolean hasBeenInvalidated()确定此开关点是否已失效。讨论:由于失效的单向性质,一旦开关点开始对
hasBeenInvalidated
返回true,它将来将始终如此。另一方面,对其他线程可见的有效开关点可能随时失效,因为另一个线程的请求。由于失效是全局和立即的操作,在有效开关点上执行此查询必须在可能导致失效的任何其他线程内部顺序化。因此,此查询可能是昂贵的。构建一个查询开关点
s
的失效状态的布尔值方法句柄的推荐方法是在constant
true 和 false 方法句柄上调用s.guardWithTest
。- 返回:
- 如果此开关点已失效,则返回true
-
guardWithTest
返回一个方法句柄,该方法句柄始终委托给目标或回退。该方法句柄将在开关点有效时始终委托给目标。之后,它将永久委托给回退。目标和回退必须具有完全相同的方法类型,生成的组合方法句柄也将具有此类型。
- 参数:
-
target
- 只要开关点有效,就由开关点选择的方法句柄 -
fallback
- 在失效后由开关点选择的方法句柄 - 返回:
- 一个组合方法句柄,始终调用目标或回退
- 抛出:
-
NullPointerException
- 如果任一参数为null -
IllegalArgumentException
- 如果两个方法类型不匹配 - 参见:
-
invalidateAll
将所有给定的开关点设置为无效状态。执行此调用后,没有线程会观察到任何开关点处于有效状态。此操作可能是昂贵的,应该谨慎使用。如果可能,应该对一组开关点进行缓冲以进行批处理处理。
如果
switchPoints
包含一个null元素,则会引发NullPointerException
。在这种情况下,一些非null元素可能在方法异常返回之前被处理。这些元素是哪些(如果有)取决于实现。讨论:出于性能原因,
invalidateAll
不是单个开关点上的虚拟方法,而是应用于一组开关点。一些实现可能会为处理一个或多个失效操作产生较大的固定开销,但对于每个额外的失效操作产生较小的增量成本。无论如何,此操作可能是昂贵的,因为其他线程可能必须以某种方式中断以使它们注意到更新的开关点状态。但可以观察到,一次调用来使多个开关点失效具有与多次调用相同的形式效果,每次调用只对其中一个开关点进行操作。实现注意:简单的
SwitchPoint
实现可以使用一个私有的MutableCallSite
来发布开关点的状态。在这样的实现中,invalidateAll
方法可以简单地更改调用站点的目标,并发出一次调用以同步所有私有调用站点。- 参数:
-
switchPoints
- 要同步的调用站点数组 - 抛出:
-
NullPointerException
- 如果switchPoints
数组引用为null或数组包含null
-