public class SwitchPoint extends Object
A SwitchPoint
是可以将状态转换发布到其他线程的对象。 切换点最初处于有效状态,但可随时更改为无效状态。 无效无法撤销。 切换点可以将守卫的一对方法句柄组合成守卫的委托者 。 守卫的委托者是一个方法句柄,它将一个旧的方法句柄委托。 切换点的状态确定两者中的哪一个获得授权。
单个切换点可用于控制任意数量的方法句柄。 (间接地,它可以控制任何数量的呼叫站点)。这是通过使用单个切换点作为工厂来完成的,将任意数量的守卫方法句柄对组合成守卫的委托者。
当一个守卫的委托者是从一个守卫的对创建的时候,该对被包裹在一个新的方法句柄M
,它与创建它的切换点永久关联。 每对由目标T
和后备F
。 当切换点有效时,M的M
委托给T
。 无效后,调用委托给F
。
无效是全局和立即的,就像每次调用M时,切换点包含一个易失性布尔变量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()); // 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])); } }
Constructor and Description |
---|
SwitchPoint()
创建一个新的切换点。
|
Modifier and Type | Method and Description |
---|---|
MethodHandle |
guardWithTest(MethodHandle target, MethodHandle fallback)
返回一个方法句柄,它始终将它们委托给目标或后备。
|
boolean |
hasBeenInvalidated()
确定此切换点是否已被无效。
|
static void |
invalidateAll(SwitchPoint[] switchPoints)
将所有给定的切换点设置为无效状态。
|
public boolean hasBeenInvalidated()
讨论:由于无效的单向性质,一旦切换点开始返回hasBeenInvalidated
,将来会一直这样做。 另一方面,由于另一个线程的请求,其他线程可见的有效切换点可能在任何时候无效。
由于无效是一个全局和立即的操作,所以在有效的切换点上执行此查询必须与可能导致无效的任何其他线程进行内部排序。 因此,该查询可能是昂贵的。 建立该查询切换点的失效状态的布尔值的方法处理的推荐方法s
是调用s.guardWithTest
上constant
真假的方法处理。
public MethodHandle guardWithTest(MethodHandle target, MethodHandle fallback)
目标和后备必须是完全相同的方法类型,结果组合方法句柄也将是这种类型。
target
- 切换点选择的方法句柄,只要它有效
fallback
- 切换点无效后选择的方法句柄
NullPointerException
- 如果任一参数为空
IllegalArgumentException
- 如果两种方法类型不匹配
MethodHandles.guardWithTest(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle)
public static void invalidateAll(SwitchPoint[] switchPoints)
这个操作可能是昂贵的,应该谨慎使用。 如果可能,应对缓冲器进行缓冲,以便对交换点集合进行批处理。
如果switchPoints
包含一个空元素,那么NullPointerException
将被提出。 在这种情况下,方法返回异常之前,可能会处理数组中的一些非空元素。 哪些元素(如果有的话)是依赖于实现的。
讨论:出于性能原因, invalidateAll
不是单个切换点上的虚拟方法,而是适用于一组切换点。 一些实现可能产生用于处理一个或多个无效操作的大的固定开销成本,但是对于每个额外的无效,都需要较小的增量成本。 在任何情况下,该操作可能是昂贵的,因为其他线程可能必须以某种方式中断,以便使它们注意更新的切换点状态。 然而,可以观察到,使多个切换点无效的单个呼叫与多个呼叫具有相同的形式效果,每个呼叫仅在一个切换点上。
实现注意:简单实现SwitchPoint
可以使用私人MutableCallSite
发布一个开关点的状态。 在这种实现中, invalidateAll
方法可以简单地更改呼叫站点的目标,并发出一个呼叫到所有私人呼叫站点的synchronize 。
switchPoints
- 要同步的呼叫站点数组
NullPointerException
- 如果
switchPoints
数组引用为空或数组包含一个空值