Module  java.base
软件包  java.lang.invoke

Class MutableCallSite

  • 已知直接子类:
    AbstractRelinkableCallSite


    public class MutableCallSite
    extends CallSite
    A MutableCallSite是一个CallSite其目标变量的行为像一个普通字段。 链接到MutableCallSiteinvokedynamic指令将对站点的当前目标的所有调用委托。 可变电话网站的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

    从以下版本开始:
    1.7
    • 构造方法详细信息

      • MutableCallSite

        public MutableCallSite​(MethodType type)
        使用给定的方法类型创建一个空白的调用站点对象。 初始目标设置为给定类型的方法句柄,如果调用该方法句柄将抛出一个IllegalStateException

        呼叫站点的类型永久设置为给定类型。

        在从引导方法返回CallSite对象之前,或者以其他方式调用该对象,通常会通过调用setTarget提供更有用的目标方法。

        参数
        type - 此呼叫站点将具有的方法类型
        异常
        NullPointerException - 如果提议的类型为空
      • MutableCallSite

        public MutableCallSite​(MethodHandle target)
        创建一个具有初始目标方法句柄的调用站点对象。 呼叫站点的类型永久设置为初始目标的类型。
        参数
        target - 将作为呼叫站点初始目标的方法句柄
        异常
        NullPointerException - 如果提议的目标为空
    • 方法详细信息

      • getTarget

        public final MethodHandle getTarget​()
        返回调用站点的目标方法,其行为类似于MutableCallSite的正常字段。

        getTarget与存储器的交互与从普通变量读取的相同,例如数组元素或非易失性非最终字段。

        特别地,当前线程可以选择从存储器重用先前读取目标的结果,并且可能无法看到另一个线程对目标的最新更新。

        Specified by:
        getTarget在类 CallSite
        结果
        此呼叫站点的链接状态,可随时间变化的方法句柄
        另请参见:
        setTarget(java.lang.invoke.MethodHandle)
      • setTarget

        public void setTarget​(MethodHandle newTarget)
        将此呼叫站点的目标方法更新为常规变量。 新目标的类型必须符合旧目标的类型。

        与存储器的交互与对普通变量的写入相同,例如数组元素或非易失性非最终字段。

        特别地,不相关的线程可能无法看到更新的目标,直到它们执行从内存读取。 可以通过将适当的操作放入引导方法和/或在任何给定的呼叫站点使用的目标方法来创建更强大的保证。

        Specified by:
        setTargetCallSite
        参数
        newTarget - 新的目标
        异常
        NullPointerException - 如果提议的新目标为null
        WrongMethodTypeException - 如果提出的新目标具有与先前目标不同的方法类型
        另请参见:
        getTarget()
      • dynamicInvoker

        public final MethodHandle dynamicInvoker​()
        生成等效于已经链接到此调用站点的invokedynamic指令的方法句柄。

        此方法等同于以下代码:

        
         MethodHandle getTarget, invoker, result;
         getTarget = MethodHandles.publicLookup().bind(this, "getTarget", MethodType.methodType(MethodHandle.class));
         invoker = MethodHandles.exactInvoker(this.type());
         result = MethodHandles.foldArguments(invoker, getTarget)
         
        Specified by:
        dynamicInvokerCallSite
        结果
        总是调用此调用站点的当前目标的方法句柄
      • syncAll

        public static void syncAll​(MutableCallSite[] sites)
        在给定数组中的每个调用站点上执行同步操作,强制所有其他线程丢弃先前从任何呼叫站点的目标加载的缓存值。

        此操作不会反转任何已经在旧目标值上启动的呼叫。 (Java仅支持forward time travel

        整体效果是迫使每个呼叫站点的所有未来读者接受最近存储的值。 (“最近”是相对于syncAll本身计算的。)相反, syncAll调用可能会阻塞,直到所有读者都以某种方式解除了每个呼叫站点目标的所有先前版本。

        为避免竞争条件,通常应以某种互斥方式执行对setTargetsyncAll通话。 请注意,读者线程可以在安装该值的setTarget调用之前(以及确认该值的syncAll之前)观察更新的目标。 另一方面,读者线程可以观察目标的先前版本,直到syncAll呼叫返回(并且在尝试传达更新版本的setTarget之后)。

        这个操作可能是昂贵的,应该谨慎使用。 如果可能的话,应该对一组呼叫站点进行缓冲以进行批处理。

        如果sites包含一个空元素,则会产生一个NullPointerException 在这种情况下,方法返回异常之前,可能会处理数组中的一些非空元素。 哪些元素(如果有的话)是依赖于实现的。

        Java内存模型详细信息

        在Java内存模型方面,此操作执行与当前线程写入易失性变量相当的同步操作,以及可能访问受影响的呼叫站点之一的每个其他线程的最终易失性读取。

        以下效果显而易见,对于每个个人呼叫站点S

        • 创建一个新的volatile变量V ,并由当前线程写入。 由JMM定义,此写入是全局同步事件。
        • 由于写入事件的线程本地排序是正常的,所以当前线程已经执行的每个动作都在易失性写入V之前被V (在某些实现中,这意味着当前线程执行全局释放操作。)
        • 具体来说,写入S的当前目标是在易失性写入V之前发生的。
        • V的易失性写入以全局同步顺序放置(以实现特定的方式)。
        • 考虑一个任意的线程T (除了当前线程)。 如果T在易失性写入V (全局同步顺序)之后执行同步动作A ,则需要查看当前目标值为S或稍后对该目标进行写入,如果它在目标上执行读取的S (此约束称为“同步顺序一致性”。)
        • JMM专门允许优化编译器来删除已知无用的变量的读取或写入。 这种缓和的读取和写入对于发生之前的关系没有影响。 不管这个事实如何,易失性的V将不会被消除,即使它的写入值是不确定的,并且其读取值不被使用。
        由于最后一点,执行的行为就好像在V的动作A之后立即执行T的易失性读取。 T的本地订购行动中,此读取发生在任何未来读取目标的S 这是因为如果执行任意地挑选的读取S的目标由T ,和强制的读V先于它,从而保证了新的目标值的通信。

        只要遵循Java内存模型的约束,实现可能会延迟完成syncAll操作,而其他线程(上述的T )继续使用S的先前值。 然而,一直鼓励实施方式避免活动锁,并最终要求所有线程考虑更新的目标。

        讨论:出于性能原因, syncAll不是单个呼叫站点上的虚拟方法,而是适用于一组呼叫站点。 一些实现可能产生用于处理一个或多个同步操作的大的固定开销成本,但是每个附加呼叫站点的增量成本较小。 在任何情况下,该操作可能是昂贵的,因为其他线程可能必须以某种方式被中断,以便使它们注意更新的目标值。 然而,可以观察到,同步多个站点的单个调用与许多调用具有相同的正式效果,每个调用仅在其中一个站点上。

        实现注意: MutableCallSite简单实现可能会为易变的调用站点的目标使用volatile变量。 在这种实现中, syncAll方法可以是无操作的,但它将符合上述JMM行为。

        参数
        sites - 要同步的呼叫站点数组
        异常
        NullPointerException - if the sites array reference is null or the array contains a null