概述
在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、privateMethodHandles.publicLookup
:获取一个可访问任何类的公共成员的Lookup对象。它没有包私有、受保护或私有成员的访问权限MethodHandles.privateLookupIn()
:获取一个对指定类的私有成员的访问权限的Lookup实例,通常用于跨模块访问的场景中,特别是在模块化系统中,允许安全地访问其他模块中的私有成员
基于MethodHandles.Lookup
,创建VarHandle的方法有:
- findVarHandle:用于创建对象中非静态字段的VarHandle。接收参数有三个,分别是接收者的class对象,字段名称和类型
- findStaticVarHandle:用于创建对象中静态字段的VarHandle。接收参数与findVarHandle一致
- unreflectVarHandle:通过反射字段Field创建VarHandle
直接使用MethodHandles,创建VarHandle的方法有:
MethodHandles.arrayElementVarHandle(int[].class)
:获取管理数组的VarhandleMethodHandles.byteArrayViewVarHandle
:- byteBufferViewVarHandle:
- filterValue
- collectCoordinates:实际上调用VarHandles提供的同名方法,下同
- insertCoordinates
- filterCoordinates
- permuteCoordinates
- dropCoordinates
VarHandles是JDK内部使用的,不对外开放的工具类。
访问类别
参考VarHandle.AccessType
源码:
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
源码:
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
等三个方法可用于反射和动态访问,能够高效、安全、精细地控制对类成员的访问。
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
参考
- VarHandle:Java9中保证变量读写可见性、有序性、原子性利器