public class MutableCallSite extends CallSite
MutableCallSite
是一个CallSite
,其目标变量的行为像普通字段。
一个invokedynamic
指令链接到MutableCallSite
委托给该网站的当前目标的所有呼叫。
该dynamic invoker一个可变的调用点也代表每次调用该网站的当前目标。
以下是将状态变量引入到方法句柄链中的可变调用站点的示例。
MutableCallSite name = new MutableCallSite(MethodType.methodType(String.class)); MethodHandle MH_name = name.dynamicInvoker(); MethodType MT_str1 = MethodType.methodType(String.class); MethodHandle MH_upcase = MethodHandles.lookup() .findVirtual(String.class, "toUpperCase", MT_str1); MethodHandle worker1 = MethodHandles.filterReturnValue(MH_name, MH_upcase); name.setTarget(MethodHandles.constant(String.class, "Rocky")); assertEquals("ROCKY", (String) worker1.invokeExact()); name.setTarget(MethodHandles.constant(String.class, "Fred")); assertEquals("FRED", (String) worker1.invokeExact()); // (mutation can be continued indefinitely)
同一个呼叫站点可以同时在几个地方使用。
MethodType MT_str2 = MethodType.methodType(String.class, String.class); MethodHandle MH_cat = lookup().findVirtual(String.class, "concat", methodType(String.class, String.class)); MethodHandle MH_dear = MethodHandles.insertArguments(MH_cat, 1, ", dear?"); MethodHandle worker2 = MethodHandles.filterReturnValue(MH_name, MH_dear); assertEquals("Fred, dear?", (String) worker2.invokeExact()); name.setTarget(MethodHandles.constant(String.class, "Wilma")); assertEquals("WILMA", (String) worker1.invokeExact()); assertEquals("Wilma, dear?", (String) worker2.invokeExact());
目标值的不同步:对可变的调用站点的目标的写入不会强制其他线程意识到更新的值。 相对于更新的呼叫站点不执行适当的同步动作的线程可能会缓存旧的目标值,并无限期地延迟其使用新的目标值。 (这是应用于对象字段的Java内存模型的正常结果。)
syncAll
操作提供了强制线程接受新的目标值的方法,即使没有其他同步。
对于经常更新的目标值,请考虑使用volatile call site 。
Constructor and Description |
---|
MutableCallSite(MethodHandle target)
创建一个具有初始目标方法句柄的调用站点对象。
|
MutableCallSite(MethodType type)
使用给定的方法类型创建一个空白的调用站点对象。
|
Modifier and Type | Method and Description |
---|---|
MethodHandle |
dynamicInvoker()
生成等效于已经链接到此调用站点的invokedynamic指令的方法句柄。
|
MethodHandle |
getTarget()
返回调用站点的目标方法,其行为类似于
MutableCallSite 的正常字段。
|
void |
setTarget(MethodHandle newTarget)
将此呼叫站点的目标方法更新为常规变量。
|
static void |
syncAll(MutableCallSite[] sites)
在给定数组中的每个调用站点上执行同步操作,强制所有其他线程丢弃先前从任何呼叫站点的目标加载的缓存值。
|
public MutableCallSite(MethodType type)
IllegalStateException
。
呼叫站点的类型永久设置为给定类型。
在CallSite
对象从引导方法返回之前,或者以其他方式调用,通常会通过调用setTarget
提供更有用的目标方法。
type
- 此呼叫站点将具有的方法类型
NullPointerException
- 如果提议的类型为空
public MutableCallSite(MethodHandle target)
target
- 将作为呼叫站点初始目标的方法句柄
NullPointerException
- 如果提出的目标为null
public final MethodHandle getTarget()
MutableCallSite
的正常字段。
getTarget
与存储器的getTarget
与从普通变量(例如数组元素或非易失性非最终字段)的读取相同。
特别地,当前线程可以选择从存储器重用先前读取目标的结果,并且可能无法看到另一个线程对目标的最新更新。
getTarget
在类别
CallSite
setTarget(java.lang.invoke.MethodHandle)
public void setTarget(MethodHandle newTarget)
与存储器的交互与对普通变量的写入相同,例如数组元素或非易失性非最终字段。
特别地,不相关的线程可能无法看到更新的目标,直到它们执行从内存读取。 可以通过将适当的操作放入引导方法和/或在任何给定的呼叫站点使用的目标方法来创建更强大的保证。
setTarget
在类别
CallSite
newTarget
- 新的目标
NullPointerException
- 如果提出的新目标为null
WrongMethodTypeException
- 如果提出的新目标具有与先前目标不同的方法类型
getTarget()
public final MethodHandle dynamicInvoker()
此方法等同于以下代码:
MethodHandle getTarget, invoker, result; getTarget = MethodHandles.publicLookup().bind(this, "getTarget", MethodType.methodType(MethodHandle.class)); invoker = MethodHandles.exactInvoker(this.type()); result = MethodHandles.foldArguments(invoker, getTarget)
dynamicInvoker
在类别
CallSite
public static void syncAll(MutableCallSite[] sites)
此操作不会反转任何已经在旧目标值上启动的呼叫。 (Java仅支持forward time travel )
整体效果是迫使每个呼叫站点的所有未来读者接受最近存储的值。 (“最近”相对于syncAll
本身而言)。相反, syncAll
通话可能会阻止,直到所有读者都以某种方式解除了每个呼叫站点目标的所有先前版本。
为了避免竞争条件, syncAll
应以某种互斥方式执行对setTarget
和syncAll
通话。 请注意,读者线程可以在安装该值的setTarget
调用之前(以及syncAll
确认该值之前)观察更新的目标。 另一方面,读者线程可以观察目标的先前版本,直到syncAll
呼叫返回(并且在尝试传达更新版本的setTarget
之后)。
这个操作可能是昂贵的,应该谨慎使用。 如果可能的话,应该对一组呼叫站点进行缓冲以进行批处理。
如果sites
包含一个空元素,那么NullPointerException
将被提出。 在这种情况下,方法返回异常之前,可能会处理数组中的一些非空元素。 哪些元素(如果有的话)是依赖于实现的。
以下效果是显而易见的,对于每个单独的呼叫站点S
:
V
,并由当前线程写入。 由JMM定义,此写入是全局同步事件。 V
之前被V
。 (在某些实现中,这意味着当前线程执行全局释放操作。) S
被取挥发性写入之前发生V
。 V
的易失性写入以全局同步顺序放置(以实现特定的方式)。 T
(不同于当前线程)。 如果T
执行的同步动作A
挥发性写入后V
(在全局同步顺序),则因此需要看到任一的当前目标S
,或到该目标以后写入时,如果它在目标执行一读的S
。 (此约束称为“同步顺序一致性”。) V
将不会被V
,即使它的写入值是不确定的,并且其读取值不被使用。 V
被执行T
其行动后立即A
。
在T
的本地订购行动中,此读取发生在任何未来读取目标之前的S
。
这是因为如果执行任意地挑选的读取S
的目标通过T
,和强制的读V
先于它,从而保证了新的目标值的通信。
只要遵循Java内存模型的约束,实现可能会延迟syncAll syncAll
完成,而其他线程( T
以上)继续使用以前的S
的目标值。 然而,一直鼓励实施方式避免活动锁,并最终要求所有线程考虑更新的目标。
讨论:出于性能原因, syncAll
不是单个呼叫站点上的虚拟方法,而是适用于一组呼叫站点。 一些实现可能产生用于处理一个或多个同步操作的大的固定开销成本,但是每个附加呼叫站点的增量成本较小。 在任何情况下,该操作可能是昂贵的,因为其他线程可能必须以某种方式被中断,以便使它们注意更新的目标值。 然而,可以观察到,同步多个站点的单个调用与许多调用具有相同的正式效果,每个调用仅在其中一个站点上。
实现注意:简单实现MutableCallSite
可以使用volatile变量的可变调用点的目标。 在这种实现中, syncAll
方法可以是无操作的,但它将符合上述JMM行为。
sites
- 要同步的呼叫站点数组
NullPointerException
- 如果
sites
数组引用为null或数组包含一个空值