我花了很多时间研究一些 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) {
// ...
}
}
}
但下一步是什么?我的意思是,如何使用这个变量句柄?你能提供任何真实的例子吗?
答案 0 :(得分:29)
例如在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);
}
此处this
指针与字段偏移量一起使用以访问字段。但这是不安全的,因为这个字段偏移可能是任何long
,你实际上可能正在访问完全不同的东西。但是这样做会带来性能上的好处(它告诉VM使用专门的CPU指令),并且因为其他人使用了sun.misc.Unsafe
,即使它是内部的,而且不安全的 API。
VarHandles的部分目的是用安全的等价物替换sun.misc.Unsafe
中的操作。这在the 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);
}
其中VALUE
是VarHandle
定义如下:
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);
}
}