JAVA 学习笔记之JEP-193(变量句柄)

1. 背景

变量句柄(Variable Handles)早在JDK9中就引入的特性,之前一直没有太关注,最近看代码的时候发现已经有项目在使用了,研究了一下并写下此笔记备忘。

2. 变量句柄的介绍与目标

目的

变量句柄的目的是定义一个标准的用法来操作对象字段和数组元素,等效于java.util.concurrent.atomic 和 sun.misc.Unsafe。并且提供了一系列标准的内存屏障操作,用于更加细粒度的指令重排序。在安全性、可用性、性能上都要优于现有的API。

目标

  • 安全性
    不能使得JVM处于内存损坏的状态,比如,一个对象的字段只能被更新为兼容的字段类型的实例
  • 健壮性
    访问字段时,遵循与getfield / putfield 字节码相同的访问规则
  • 性能
    性能指标必须与sun.misc.Unsafe的等效方法相同或者相似
  • 可用性
    API必须优于sun.misc.UnsafeAPI

动机

随着Java中的并发和并行编程的不断扩大,我们经常会需要对某个类的字段进行原子或有序操作,但是 JVM 对Java开发者所开放的权限非常有限。例如:如果要原子性地增加某个字段的值,到目前为止我们可以使用下面三种方式:

  • 使用AtomicInteger来达到这种效果,这种间接管理方式增加了空间开销,还会导致额外的并发问题;
  • 使用原子性的FieldUpdaters,操作开销也会更大;
  • 使用sun.misc.Unsafe提供的JVM内置函数API,虽然这种方式比较快,但它会损害安全性和可移植性。

sun.misc.Unsafe等类对于模块系统和JVM封装性的损害

由于性能原因,JDK的一些内部类被许多库广泛使用。显而易见这种做法是不对的,但在某些情况下这是唯一的选择。一个著名的示例是sun.misc.Unsafe类,它可以绕过Java的内存模型和其他安全网执行一些低级操作,而JDK之外的库无法实现相同的功能。
对于这些使用频率比较高的内部API来说,由于其在现实世界中的作用太大而无法忽视,特别是当它们所提供的功能没有替代方案时更是如此。考虑到这一点,JDK9模块系统达成了妥协。

由于这些方法和类主要不是为在JDK之外使用而设计的,因此JDK9中将它们移动到名为jdk.unsupported的模块中。这表明在未来的Java版本中,该模块中的类将被其他API所替换。

而jdk.unsupported模块的替代者之一,就是变量句柄。

3. 变量句柄的使用

3.1 创建

public class TestBO {
	int testInt;

	public int getTestInt() {
		return testInt;
	}

	public void setTestInt(int testInt) {
		this.testInt = testInt;
	}
}

测试用的JAVA POJO

	private static final VarHandle VAR_HANDLE;
	
	static {
        try {
        	VAR_HANDLE = MethodHandles.lookup().findVarHandle(TestBO.class, "testInt", int.class); //or
        } catch (ReflectiveOperationException e) {
            throw new Error(e);
        }
    }

使用方法句柄MethodHandles的内部类Lookup创建变量句柄。
Lookup与反射机制的区别在于,反射机制每次调用时,都会进行访问检查,而Lookup仅在创建时进行访问检查。

3.2 原子加

	public static void main(String[] args) {
		TestBO testBO = new TestBO();
		System.out.println(VAR_HANDLE.getAndAdd(testBO, 10));
	}

当我们使用变量句柄进行volatile访问时,不需要给变量加上volatile修饰符。
事实上,如果此时变量即使拥有volatile修饰符,也会被忽略。

3.3 CAS

	public static void main(String[] args) {
		TestBO testBO = new TestBO();

		System.out.println(VAR_HANDLE.compareAndSet(testBO, 10, 120));
		System.out.println(VAR_HANDLE.get(testBO));
	}

3.4 内存屏障

变量句柄提供了一整套静态的内存屏障方法,用于精细化的并发控制

    @ForceInline
    public static void fullFence() {
        UNSAFE.fullFence();
    }

    @ForceInline
    public static void acquireFence() {
        UNSAFE.loadFence();
    }

    @ForceInline
    public static void releaseFence() {
        UNSAFE.storeFence();
    }

    @ForceInline
    public static void loadLoadFence() {
        UNSAFE.loadLoadFence();
    }

    @ForceInline
    public static void storeStoreFence() {
        UNSAFE.storeStoreFence();
    }

ForceInline 注解标识此方法将被强制内联,而忽略实时编译器级别和回边计数器计数。
以上方法底层仍然调用了UNSAFE类的内存屏障方法,与C++11的内存屏障封装相对应。
atomic_thread_fence(memory_order_acquire)
atomic_thread_fence(memory_order_release)
atomic_thread_fence(memory_order_seq_cst)

4. 参考链接

http://openjdk.java.net/jeps/193

猜你喜欢

转载自blog.csdn.net/a860MHz/article/details/84592166