Java 9 VarHandle

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());
    }
}

posted on 2020-08-10 17:53  caffebabe  阅读(726)  评论(0编辑  收藏  举报