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

Ole*_*hov 28 java java-9

我花了很多时间研究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) {
            // ...
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Jor*_*nee 32

例如,它用于AtomicReference以前使用Java 8的地方sun.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);
}
Run Code Online (Sandbox Code Playgroud)

这里this指针与字段偏移一起使用以访问字段.但这是不安全的,因为这个字段偏移可能是任何long,你实际上可能正在访问完全不同的东西.但是以这种方式实现性能优势(它告诉VM使用专门的CPU指令),并且因为其他人已经使用过,sun.misc.Unsafe即使它是一个内部的,不安全的 API.

VarHandles的部分目的是sun.misc.Unsafe用安全的等价物替换操作.JEP中说明了这一点:

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

目标:

以下是必修目标:

  • 安全.必须无法将Java虚拟机置于损坏的内存状态.例如,对象的字段只能使用可转换为字段类型的实例进行更新,或者只有在数组索引位于数组边界内时才能在数组中访问数组元素.

  • 诚信.除了无法更新对象的最终字段的约束之外,对对象字段的访问遵循与getfield和putfield字节代码相同的访问规则.(注意:此类安全性和完整性规则也适用于MethodHandles,允许对字段进行读取或写入访问.)

  • 性能.性能特征必须与等效的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);
}
Run Code Online (Sandbox Code Playgroud)

这样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);
    }
}
Run Code Online (Sandbox Code Playgroud)