在Java 9中使用VarHandle的正确方法?

2022-09-01 06:50:37

我花了很多时间研究Java 9的一些新功能,但我没有找到任何有用和实际的例子。

考虑下一个创建 VarHandle 的代码片段:

class Counter {
    int i;
}

class VarHandleInAction {
    static final VarHandle VH_COUNTER_FIELD_I;

    static {
        try {
            VH_COUNTER_FIELD_I = MethodHandles.lookup()
                .in(Counter.class)
                .findVarHandle(Counter.class, "i", int.class);
        } catch (Exception e) {
            // ...
        }
    }
}

但下一步是什么?我的意思是,如何使用这个变量句柄?您能提供任何真实的例子吗?


答案 1

例如,它用于 ,而以前在 Java 8 中使用:AtomicReferencesun.misc.Unsafe

public final void lazySet(V newValue) {
    unsafe.putOrderedObject(this, valueOffset, newValue);
}

public final boolean compareAndSet(V expect, V update) {
    return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
}

此处,指针与字段偏移量一起使用以访问字段。但这是不安全的,因为此字段偏移量可以是任何 ,并且您实际上可能正在访问完全不同的东西。然而,这样做有性能优势(例如,它告诉VM使用专门的CPU指令),并且由于其他人已经使用过,即使它是一个内部的,不安全的API。thislongsun.misc.Unsafe

的部分目的是将 中的操作替换为安全的等效项。这在 JEP 中指出:VarHandlesun.misc.Unsafe

定义一个标准方法来调用各种java.util.concurrent.atomic和sun.misc.Unsafe操作的等效项...

目标:

以下是必需的目标:

  • 安全。一定不可能将 Java 虚拟机置于损坏的内存状态。例如,对象的字段只能使用可强制转换为字段类型的实例进行更新,或者如果数组索引在数组边界内,则只能在数组内访问数组元素。

  • 正直。对对象字段的访问遵循与 getfield 和 putfield 字节码相同的访问规则,此外还有无法更新对象的最终字段的约束。(注意:此类安全和完整性规则也适用于授予对字段的读取或写入访问权限的方法处理程序。

  • 性能。性能特征必须与等效的 sun.misc.Unsafe 操作相同或相似(具体而言,生成的汇编程序代码应与某些无法折叠的安全检查模数几乎相同)。

  • 可用性。该 API 必须优于 sun.misc.Unsafe API。

所以在Java 9中,这些方法看起来像这样:

public final void lazySet(V newValue) {
    VALUE.setRelease(this, newValue);
}

public final boolean compareAndSet(V expectedValue, V newValue) {
    return VALUE.compareAndSet(this, expectedValue, newValue);
}

其中有如下定义:VALUEVarHandle

private static final VarHandle VALUE;
static {
    try {
        MethodHandles.Lookup l = MethodHandles.lookup();
        VALUE = l.findVarHandle(AtomicReference.class, "value", Object.class);
    } catch (ReflectiveOperationException e) {
        throw new Error(e);
    }
}

答案 2

推荐