VarHandle是什么?
在看AtomicInteger源码的时候,有这么一句话:
See the VarHandle specification for descriptions of the properties of atomic accesses.
有关原子访问属性的描述,请参阅VarHandle规范?以前不都是Unsafe吗,怎么现在让我参考VarHandle?点进去一看,原来从Java 9开始,会尽可能用VarHandle替代Unsafe的功能。
JDK文档对VarHandle的描述如下:
A VarHandle is a dynamically strongly typed reference to a variable, or to a parametrically-defined family of variables, including static fields, non-static fields, array elements, or components of an off-heap data structure. Access to such variables is supported under various access modes, including plain read/write access, volatile read/write access, and compare-and-set.
VarHandle是对变量的类型化引用,其功能是提供在不同的访问模式下对该变量的读写访问。
变量包括静态变量,非静态变量,数组元素,堆外数据结构的成员(ByteBuffer吗?)。
访问模式包括普通读写(plain read/write access),volatile语义读写(volatile read/write access)和CAS操作(compare-and-set)。
那么为什么要提供VarHandle呢?
在Java 8之前,Java提供的底层编程接口有两种,一是JNI,一是Unsafe。相比JNI,Unsafe对底层操作系统的依赖更小点,但还是有问题。sun开头的包并非Oracle官方维护的API,Oracle还计划在未来的JDK版本中删除这些API。所以使用Unsafe的代码可能会有兼容性和移植性的问题。于是有了JEP 193,该提案主要就是针对这个问题,并且,它还提出了对象字段(或类静态字段)原子操作和不安全操作的标准化接口,这就是VarHandle。
请参考JEP 193
简而言之,尽可能用VarHandle的API来替代Unsafe的API。
访问模式(Access Modes)
VarHandle文档中定义了以下几种访问类别:plain、opaque、release/acquire、volatile。它们提供了由低到高的一致性保证级别:
- plain类型不确保内存可见性,opaque、release/acquire、volatile是可以保证内存可见的。
- opaque保证程序执行顺序,但不保证其它线程的可见顺序。
- release/acquire 保证程序执行顺序,setRelease确保前面的load和store不会被重排序到后面,但不确保后面的load和store重排序到前面;getAcquire确保后面的load和store不会被重排序到前面,但不确保前面的load和store被重排序。
- volatile确保程序执行顺序,且保证变量之间不被重排序。
VarHandle定义了以下五种访问模式,结合上面的一致性保证级别,提供了超多的方法。
- Read access mode:get, getVolatile, getAcquire 和 getOpaque.
- Write access mode:set, setVolatile, setRelease 和 setOpaque.
- Atomic update access mode:compareAndSet, compareAndExchangeAcquire, compareAndExchange, compareAndExchangeRelease, getAndSet, getAndSetAcquire, getAndSetRelease, etc.
- Numeric atomic update access mode:getAndAdd, getAndAddAcquire, getAndAddRelease.
- Bitwise atomic update access mode:getAndBitwiseOr, getAndBitwiseOrAcquire, getAndBitwiseOrRelease, getAndBitwiseAnd, getAndBitwiseAndAcquire, getAndBitwiseAndRelease, getAndBitwiseXor, getAndBitwiseXorAcquire, getAndBitwiseXorRelease, etc.
使用示例
Java 9之前,我们可以使用AtomicInteger来实现线程安全的累加器:
public class Counter {
private AtomicInteger count = new AtomicInteger(0);
public void increment() {
this.count.getAndIncrement();
}
public int get() {
return this.count.get();
}
}
现在,改写成用VarHandle实现:
public class Counter {
private static int count = 0;
public static void increment() throws IllegalAccessException, NoSuchFieldException {
VarHandle countHandle = MethodHandles.privateLookupIn(Counter.class, MethodHandles.lookup())
.findStaticVarHandle(Counter.class, "count", int.class);
countHandle.getAndAdd(1);
}
public static int get() {
return count;
}
public static void main(String[] args) {
int threadNum = 500;
Thread[] threads = new Thread[threadNum];
for (int i = 0; i < threadNum; i++) {
threads[i] = new Thread(() -> {
try {
Counter.increment();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
});
}
for (Thread t : threads) {
t.start();
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Counter.get());
}
}