前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JDK 9新特性之VarHandle

JDK 9新特性之VarHandle

原创
作者头像
johnny666
发布2024-10-01 00:38:22
730
发布2024-10-01 00:38:22
举报
文章被收录于专栏:Java

概述

ConcurrentSkipListMap里看到VarHandle,记录一下学习笔记。

VarHandle,变量句柄,是新的原子访问属性规范,JDK8以前都是通过sun.misc.Unsafe实现原子属性访问。见名知意,Unsafe是不安全API,理解不透彻使用不正确,会有意想不到的问题。从JDK9开始,会尽可能使用VarHandle代替Unsafe,实际上VarHandle内部有几个内存屏障相关的方法还是基于Unsafe。可以说,Unsafe是更底层的API,建议使用VarHandle而不是Unsafe。

VarHandle提供一系列标准的内存屏障操作,用于更加细粒度的内存排序控制,可以针对特定的字段或变量使用VarHandle,而不需要对整个对象或代码块进行同步。在安全性、可用性、性能上都要优于现有的API。

VarHandle可与各种类型的变量一起使用,包括基本数据类型、引用类型、数组等,支持在不同访问模式下对这些类型变量的访问,包括简单的read/write访问,volatile read/write访问,和CAS(compare-and-set)等。这使得VarHandle在处理不同类型的并发问题时都非常灵活和方便。

体系

VarHandle是一个抽象基类,实现类有:

  • IndirectVarHandle
  • LazyInitializingVarHandle

针对不同的数据类型,如8种基础数据类型(int、byte、short、char、boolean、long、float、double),VarHandle都提供支持:

  • VarHandleBytes
  • VarHandleChars
  • VarHandleShorts
  • VarHandleBooleans
  • VarHandleInts
  • VarHandleLongs
  • VarHandleFloats
  • VarHandleDoubles
  • VarHandleReferences
  • VarHandleByteArray

VarHandleByteArrayBase也是一个抽象基类,其实现类有:

  • VarHandleByteArrayAsChars
  • VarHandleByteArrayAsDoubles
  • VarHandleByteArrayAsFloats
  • VarHandleByteArrayAsInts
  • VarHandleByteArrayAsLongs
  • VarHandleByteArrayAsShorts

且这些实现类基于某种机制(待调研)自动生成的,类结构一样一样:

代码语言:java
复制
// -- This file was mechanically generated: Do not edit! -- //
final class VarHandleByteArrayAsChars extends VarHandleByteArrayBase {
	static final JavaNioAccess NIO_ACCESS = SharedSecrets.getJavaNioAccess();
	
	static final int ALIGN = Character.BYTES - 1;
	
	static final ScopedMemoryAccess SCOPED_MEMORY_ACCESS = ScopedMemoryAccess.getScopedMemoryAccess();
	
	@ForceInline
	static char convEndian(boolean big, char n) {
		return big == BE ? n : Character.reverseBytes(n);
	}
	
	static abstract sealed class ByteArrayViewVarHandle extends VarHandle {
	}
	
	static final class ArrayHandle extends ByteArrayViewVarHandle {
		// 省略方法若干,下同
		static final VarForm FORM = new VarForm(ArrayHandle.class, byte[].class, char.class, int.class);
	}
	
	static final class ByteBufferHandle extends ByteArrayViewVarHandle {
		static final VarForm FORM = new VarForm(ByteBufferHandle.class, ByteBuffer.class, char.class, int.class);
	}
}

源码还是涉及到很多API,如ScopedMemoryAccess、VarForm、SharedSecrets、JavaNioAccess、Unsafe、MemorySegment。

JDK源码真是博大精深啊,简历上写【精通Java】的人脸皮真厚,我也是。

创建VarHandle

创建VarHandle实例之前,可以先获取MethodHandles.Lookup实例:

  • MethodHandles.Lookup:是一个工厂类,用于创建方法句柄(Method Handles),这些句柄可用于访问类的字段、方法和构造函数。Lookup对象的权限和功能取决于它的创建上下文(通常是某个类),以及相应的访问权限。提供对不同访问级别成员的访问,包括:public、package、protected、private
  • MethodHandles.publicLookup:获取一个可访问任何类的公共成员的Lookup对象。它没有包私有、受保护或私有成员的访问权限
  • MethodHandles.privateLookupIn():获取一个对指定类的私有成员的访问权限的Lookup实例,通常用于跨模块访问的场景中,特别是在模块化系统中,允许安全地访问其他模块中的私有成员

基于MethodHandles.Lookup,创建VarHandle的方法有:

  • findVarHandle:用于创建对象中非静态字段的VarHandle。接收参数有三个,分别是接收者的class对象,字段名称和类型
  • findStaticVarHandle:用于创建对象中静态字段的VarHandle。接收参数与findVarHandle一致
  • unreflectVarHandle:通过反射字段Field创建VarHandle

直接使用MethodHandles,创建VarHandle的方法有:

  • MethodHandles.arrayElementVarHandle(int[].class):获取管理数组的Varhandle
  • MethodHandles.byteArrayViewVarHandle
  • byteBufferViewVarHandle:
  • filterValue
  • collectCoordinates:实际上调用VarHandles提供的同名方法,下同
  • insertCoordinates
  • filterCoordinates
  • permuteCoordinates
  • dropCoordinates

VarHandles是JDK内部使用的,不对外开放的工具类。

访问类别

参考VarHandle.AccessType源码:

代码语言:java
复制
enum AccessType {
	GET(Object.class),
	SET(void.class),
	COMPARE_AND_SET(boolean.class),
	COMPARE_AND_EXCHANGE(Object.class),
	GET_AND_UPDATE(Object.class);
}
  • GET get getVolatile:相当于获取后加LoadLoad + LoadStore getAcquire getOpaque
  • SET set setVolatile setRelease setOpaque
  • COMPARE_AND_SET compareAndSet weakCompareAndSetPlain weakCompareAndSet weakCompareAndSetAcquire weakCompareAndSetRelease
  • COMPARE_AND_EXCHANGE compareAndExchange compareAndExchangeAcquire compareAndExchangeRelease
  • GET_AND_UPDATE getAndAdd getAndAddAcquire getAndAddRelease getAndBitwiseOr getAndBitwiseOrRelease getAndBitwiseOrAcquire getAndBitwiseAnd getAndBitwiseAndRelease getAndBitwiseAndAcquire getAndBitwiseXor getAndBitwiseXorRelease getAndBitwiseXorAcquire

访问模式

参考VarHandle.AccessMode源码:

代码语言:java
复制
public enum AccessMode {
	// 省略引用AccessType
	GET("get", GET),
	SET("set", SET),
	GET_VOLATILE("getVolatile", GET),
	SET_VOLATILE("setVolatile", SET),
	GET_ACQUIRE("getAcquire", GET),
	SET_RELEASE("setRelease", SET),
	GET_OPAQUE("getOpaque", GET),
	SET_OPAQUE("setOpaque", SET),
	COMPARE_AND_SET("compareAndSet", COMPARE_AND_SET),
	COMPARE_AND_EXCHANGE("compareAndExchange", COMPARE_AND_EXCHANGE),
	COMPARE_AND_EXCHANGE_ACQUIRE("compareAndExchangeAcquire", COMPARE_AND_EXCHANGE),
	COMPARE_AND_EXCHANGE_RELEASE("compareAndExchangeRelease", COMPARE_AND_EXCHANGE),
	WEAK_COMPARE_AND_SET_PLAIN("weakCompareAndSetPlain", COMPARE_AND_SET),
	WEAK_COMPARE_AND_SET("weakCompareAndSet", COMPARE_AND_SET),
	WEAK_COMPARE_AND_SET_ACQUIRE("weakCompareAndSetAcquire", COMPARE_AND_SET),
	WEAK_COMPARE_AND_SET_RELEASE("weakCompareAndSetRelease", COMPARE_AND_SET),
	GET_AND_SET("getAndSet", GET_AND_UPDATE),
	GET_AND_SET_ACQUIRE("getAndSetAcquire", GET_AND_UPDATE),
	GET_AND_SET_RELEASE("getAndSetRelease", GET_AND_UPDATE),
	GET_AND_ADD("getAndAdd", GET_AND_UPDATE),
	GET_AND_ADD_ACQUIRE("getAndAddAcquire", GET_AND_UPDATE),
	GET_AND_ADD_RELEASE("getAndAddRelease", GET_AND_UPDATE),
	GET_AND_BITWISE_OR("getAndBitwiseOr", GET_AND_UPDATE),
	GET_AND_BITWISE_OR_RELEASE("getAndBitwiseOrRelease", GET_AND_UPDATE),
	GET_AND_BITWISE_OR_ACQUIRE("getAndBitwiseOrAcquire", GET_AND_UPDATE),
	GET_AND_BITWISE_AND("getAndBitwiseAnd", GET_AND_UPDATE),
	GET_AND_BITWISE_AND_RELEASE("getAndBitwiseAndRelease", GET_AND_UPDATE),
	GET_AND_BITWISE_AND_ACQUIRE("getAndBitwiseAndAcquire", GET_AND_UPDATE),
	GET_AND_BITWISE_XOR("getAndBitwiseXor", GET_AND_UPDATE),
	GET_AND_BITWISE_XOR_RELEASE("getAndBitwiseXorRelease", GET_AND_UPDATE),
	GET_AND_BITWISE_XOR_ACQUIRE("getAndBitwiseXorAcquire", GET_AND_UPDATE),
	;

	private final String methodName;
	private final AccessType at;
}

有4种:

  • plain:普通访问,无方法后缀,不保证内存可见性及执行顺序
  • opaque:保证执行顺序,不保证内存可见性
  • acquire/release:保证执行顺序,setRelease确保前面的load和store不会被重排序到后面,但不确保后面的load和store重排序到前面;getAcquire确保后面的load和store不会被重排序到前面,但不确保前面的load和store被重排序。
  • volatile:保证执行顺序,且保证变量不会被重排

内存屏障

5个方法均为静态方法,看源码,实际上还是调用Unsafe。

方法名

方法描述

fullFence

保证方法调用之前的所有读写操作不会被方法后的读写操作重排

acquireFence

保证方法调用之前的所有读操作不会被方法后的读写操作重排

releaseFence

保证方法调用之前的所有读写操作不会被方法后的写操作重排

loadLoadFence

保证方法调用之前的所有读操作不会被方法后的读操作重排

storeStoreFence

保证方法调用之前的所有写操不会被方法后的写操作重排

实战

基本使用

代码语言:java
复制
@Slf4j
public class VarHandleDemo {
	private int x; // 共享变量
	private static VarHandle X;

	static {
		try {
			// 初始化VarHandle:需指定要保护的变量,某个类中的 共享变量名名 共享变量类型。
			// 注意到这里int基础类型也可以获取到class类型
			X = MethodHandles.lookup().findVarHandle(VarHandleDemo.class, "x", int.class);
		} catch (NoSuchFieldException | IllegalAccessException e) {
			log.error("fail: {}", e.getMessage());
		}
	}

	public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
		VarHandleDemo obj = new VarHandleDemo();
		X.set(obj, 10);
		Object o = X.get(obj);
		System.out.println(o);
	}
}

高效反射

MethodHandles.Lookup等三个方法可用于反射和动态访问,能够高效、安全、精细地控制对类成员的访问。

代码语言:java
复制
public class VarHandleDemo {
	public int publicVar = 1;
	protected int protectedVar = 2;
	private int privateVar = 3;
	public int[] arrayData = new int[]{1, 2, 3};
	
	@Override
	public String toString() {
		return "VarHandleDemo {" + "publicVar=" + publicVar + ", protectedVar=" + protectedVar + ", privateVar=" + privateVar + ", arrayData=" + Arrays.toString(arrayData) + '}';
	}
	
	private static void protectedDemo() throws NoSuchFieldException, IllegalAccessException {
		VarHandleDemo demo = new VarHandleDemo();
		VarHandle varHandle = MethodHandles.privateLookupIn(VarHandleDemo.class, MethodHandles.lookup()).findVarHandle(VarHandleDemo.class, "protectedVar", int.class);
		VarHandle varHandle0 = MethodHandles.privateLookupIn(VarHandleDemo.class, MethodHandles.lookup()).findVarHandle(VarHandleDemo.class, "publicVar", int.class);
		// publicLookup 访问 private 属性,报错:IllegalAccessException: member is private
		// VarHandle varHandle1 = MethodHandles.publicLookup().in(VarHandleDemo.class).findVarHandle(VarHandleDemo.class, "privateVar", int.class);
		// publicLookup 访问 protected 属性,报错:IllegalAccessException: member is protected
		// VarHandle varHandle2 = MethodHandles.publicLookup().in(VarHandleDemo.class).findVarHandle(VarHandleDemo.class, "protectedVar", int.class);
	    varHandle0.set(demo, 22);
	    System.out.println(demo);
	}


	
	public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
		protectedDemo();
		arrayDemo();
	}
}

CAS

简单示例:

代码语言:java
复制
private static void arrayDemo() {
	VarHandleDemo demo = new VarHandleDemo();
	// int数组class
	VarHandle arrayVarHandle = MethodHandles.arrayElementVarHandle(int[].class);
	// 第0个索引,如果是1,则更新为11
	arrayVarHandle.compareAndSet(demo.arrayData, 0, 1, 11);
	// 第1个索引,如果是3(本来是2),则更新为22(不会更新)
	arrayVarHandle.compareAndSet(demo.arrayData, 1, 3, 22);
	System.out.println(demo);
}

按位更新

内存屏障

高级volatile

参考

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概述
    • 体系
      • 创建VarHandle
        • 访问类别
          • 访问模式
            • 内存屏障
            • 实战
              • 基本使用
                • 高效反射
                  • CAS
                    • 按位更新
                      • 内存屏障
                        • 高级volatile
                        • 参考
                        领券
                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档