一、VarHandle概述
1.1 什么是VarHandle?
VarHandle (变量句柄)是Java 9引入的一个革命性的并发编程工具,属于java.lang.invoke包。它提供了一种对变量(对象字段、数组元素、静态变量等)进行低级别、高性能原子操作 的机制。
MethodHandles 则是在Java 1.7就引入了的方法句柄工具,提供了一系列方法用来便捷的操作方法调用、构造方法调用、字段的get/set等,在本文中我们将使用MethodHandles来操作VarHandle。
1.2 VarHandle的设计目标
-
替代Unsafe API:解决Unsafe类不安全、容易出错的问题
-
性能优化:比Atomic类提供更细粒度的控制,性能更好
-
内存控制:提供更强的内存屏障和访问模式控制能力
-
标准化:提供标准化的内存操作API,减少平台依赖
-
VarHandle不是用来替代java.lang.reflect等API的
二、VarHandle的核心特性
2.1 支持的操作类型
VarHandle支持多种类型的变量访问:
| 变量类型 | 描述 | 示例 |
|---|---|---|
| 实例字段 | 对象的非静态字段 | obj.field |
| 静态字段 | 类静态字段 | ClassName.staticField |
| 数组元素 | 数组中的元素 | array[index] |
| 内存区域 | 堆外内存访问 | Off-heap Memory |
2.2 访问模式(Access Modes)
这是VarHandle最强大的特性之一,提供了丰富的访问模式:
java
// 1. 普通访问模式
get() // 普通读
set(newValue) // 普通写
// 2. volatile访问模式
getVolatile() // volatile读
setVolatile(newValue) // volatile写
// 3. 原子访问模式
compareAndSet(expected, newValue) // CAS操作
getAndSet(newValue) // 原子交换
getAndAdd(delta) // 原子加
getAndBitwiseAnd(mask) // 原子位与
// 更多原子操作...
// 4. 获取-修改模式
getAcquire() // 获取屏障后的读取
setRelease(value) // 释放屏障前的写入
三、VarHandle的创建和使用
3.1 创建VarHandle实例
方式一:通过MethodHandles.lookup()
java
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
public class VarHandleDemo {
private int count;
private static final VarHandle COUNT_HANDLE;
static {
try {
MethodHandles.Lookup lookup = MethodHandles.lookup();
COUNT_HANDLE = lookup.findVarHandle(
VarHandleDemo.class,
"count",
int.class
);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new ExceptionInInitializerError(e);
}
}
}
方式二:通过MethodHandles.arrayElementVarHandle()
java
// 创建数组访问的VarHandle
VarHandle arrayHandle = MethodHandles.arrayElementVarHandle(int[].class);
int[] array = new int[10];
// 原子操作数组元素
arrayHandle.compareAndSet(array, 0, 0, 100); // 将array[0]从0改为100
3.2 访问模块外私有字段:privateLookupIn方法
Java模块系统引入了更强的封装性,使用普通MethodHandles.lookup()无法访问模块外部的私有字段。为了访问其他模块的私有字段,需要使用privateLookupIn()方法:
java
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
public class CrossModuleVarHandleExample {
// 目标类的假设结构(可能在另一个模块中)
public static class TargetClass {
private int privateField;
}
public static void main(String[] args) throws Exception {
// 创建目标实例
TargetClass target = new TargetClass();
try {
// 使用privateLookupIn获取有PRIVATE权限的Lookup对象
MethodHandles.Lookup privateLookup = MethodHandles
.privateLookupIn(TargetClass.class, MethodHandles.lookup());
// 创建私有字段的VarHandle
VarHandle privateFieldHandle = privateLookup
.findVarHandle(TargetClass.class, "privateField", int.class);
// 访问私有字段
privateFieldHandle.set(target, 42);
int value = (int) privateFieldHandle.get(target);
System.out.println("Private field value: " + value); // 输出: Private field value: 42
// 原子操作私有字段
privateFieldHandle.compareAndSet(target, 42, 100);
System.out.println("Updated value: " + privateFieldHandle.get(target));
} catch (IllegalAccessException e) {
System.err.println("没有权限访问私有字段: " + e.getMessage());
}
}
}
UnSafeAPI想要直接跨模块时,目前暂时可以通过强制修改Class.module为基础模块来强行反射调用非导出模块的方法!(JDK25后续某个JAVA版本将会彻底移除该方法,非标准用法,仅用于临时解决问题,不建议长期使用)
java
Class clz = YourTargetClass.class;
Field field = Class.forName("sun.misc.Unsafe").getDeclaredField("theUnsafe");
field.setAccessible(true);
Unsafe unsafe = (Unsafe) field.get(null);
unsafe.putObject(clz, unsafe.objectFieldOffset(Class.class.getDeclaredField("module")), Object.class.getModule());
Method method = clz.getDeclaredMethod("class_name");
method.setAccessible(true);
method.invoke();
重要注意事项:
- 模块权限要求:需要模块在module-info.java中声明适当的opens语句:
java
// module-info.java
module com.example.targetmodule {
opens com.example.package to com.example.callingmodule;
}
-
权限检查 :调用者必须拥有对目标类的访问权限,否则会抛出
IllegalAccessException -
安全性:这比直接使用Reflection更安全,因为它保留了类型安全检查
3.3 MethodHandles调用方法
MethodHandles不仅可以访问私有字段,还可以高效地查找和调用私有方法和构造函数。相比于传统的java.lang.reflect反射API,通过MethodType定义方法的返回值和参数列表,MethodHandles具有更好的性能和类型安全性。
3.3.1 使用MethodHandles调用私有方法
java
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
public class MethodHandlesPrivateMethodExample {
public static class PrivateMethodClass {
private String secretMethod(String input) {
return "Secret: " + input.toUpperCase();
}
private static int privateStaticMethod(int a, int b) {
return a * b;
}
}
public static void main(String[] args) throws Throwable {
PrivateMethodClass instance = new PrivateMethodClass();
// 1. 获取Lookup对象,可以访问私有方法
MethodHandles.Lookup lookup = MethodHandles.lookup();
// 2. 查找并调用私有实例方法
// create MethodType: 返回值类型 + 参数类型
MethodType methodType = MethodType.methodType(String.class, String.class);
MethodHandle privateMethodHandle = lookup.findVirtual(
PrivateMethodClass.class,
"secretMethod",
methodType
);
// 调用私有实例方法
String result = (String) privateMethodHandle.invoke(instance, "hello");
System.out.println("Private method result: " + result); // 输出: Secret: HELLO
// 调用构造函数
Object instanceByLookup = MethodHandles.lookup()
.findConstructor(PrivateMethodClass.class, MethodType.methodType(void.class))
.invoke();
// 也可以bind对象后调用方法
result = (String)privateMethodHandle.bindTo(instanceByLookup).invokeWithArguments("world");
System.out.println("Private method result: " + result); // 输出: Secret: WORLD
// 3. 查找并调用私有静态方法
MethodType staticMethodType = MethodType.methodType(int.class, int.class, int.class);
MethodHandle privateStaticMethodHandle = lookup.findStatic(
PrivateMethodClass.class,
"privateStaticMethod",
staticMethodType
);
// 调用私有静态方法
int staticResult = (int) privateStaticMethodHandle.invoke(10, 20);
System.out.println("Private static method result: " + staticResult); // 输出: 200
}
}
3.3.2 MethodType详解
MethodType是MethodHandles中描述方法签名的重要类:
java
// 创建MethodType的几种方式
// 方法签名:String method(int a, double b)
MethodType mt1 = MethodType.methodType(String.class, int.class, double.class);
// 无参数静态方法:static void staticMethod()
MethodType mt2 = MethodType.methodType(void.class);
// 构造函数:Object(int arg)
MethodType mt3 = MethodType.methodType(void.class, int.class);
3.4 MethodHandles vs Reflection 性能对比
MethodHandles在性能上明显优于传统的反射API:
| 特性 | Reflection | MethodHandles |
|---|---|---|
| 性能 | 较慢,每次调用都需安全检查 | 快,JVM可以优化 |
| 类型安全 | 弱类型,运行时错误 | 强类型,编译期检查 |
| 内存屏障 | 不支持 | 支持内存屏障控制 |
| 可优化性 | JIT优化受限 | JIT友好,可内联 |
| 易用性 | 简单直观 | 相对复杂 |
3.5 实际使用示例
java
// 复杂的计数器实现
public class AtomicCounter {
private long counter = 0;
private static final VarHandle COUNTER_HANDLE;
static {
try {
COUNTER_HANDLE = MethodHandles.lookup()
.findVarHandle(AtomicCounter.class, "counter", long.class);
} catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
public long increment() {
long current, next;
do {
current = COUNTER_HANDLE.getVolatile(this);
next = current + 1;
} while (!COUNTER_HANDLE.compareAndSet(this, current, next));
return next;
}
public long get() {
return COUNTER_HANDLE.getVolatile(this);
}
}
四、VarHandle与内存模型
4.1 内存屏障(Memory Barriers)
VarHandle通过访问模式提供了细粒度的内存屏障控制:
| 屏障类型 | 描述 | 对应访问模式 |
|---|---|---|
| plain | 无内存屏障 | get(), set() |
| opaque | 保证原子性 | getOpaque(), setOpaque() |
| release-acquire | 释放-获取屏障 | setRelease(), getAcquire() |
| volatile | 完全volatile语义 | getVolatile(), setVolatile() |
4.2 访问模式的内存语义
不同的访问模式对应不同的内存排序保证:
-
Plain模式:最低级别的保证,用于性能优化的场景
-
Opaque模式:保证原子性,但不保证顺序
-
Acquire/Release模式:提供happens-before关系的保证
-
Volatile模式:完全的volatile语义,最强的保证
五、性能对比与分析
5.1 VarHandle vs AtomicReference
根据性能测试结果显示:
java
// AtomicReference方式
AtomicReference<String> atomicRef = new AtomicReference<>("initial");
atomicRef.compareAndSet("initial", "new");
// VarHandle方式
private String value = "initial";
private static final VarHandle VALUE_HANDLE;
// 性能对比:
// VarHandle在高并发场景下通常比AtomicReference快20-30%
// 原因是VarHandle避免了对象包装的开销
5.2 VarHandle vs Unsafe
VarHandle在设计上解决了Unsafe的多个问题:
| 特性 | Unsafe | VarHandle |
|---|---|---|
| 安全性 | 需要特殊权限 | 标准API,安全可控 |
| 可移植性 | 平台相关 | 平台无关,标准实现 |
| 内存模型 | 无保证 | 符合JMM规范 |
| 类型安全 | 弱类型 | 强类型检查 |
| 易用性 | 复杂 | 相对简单 |
5.3 MethodHandles vs Reflection性能测试
根据实际测试:
-
MethodHandles.invoke()比Method.invoke()快2-3倍 -
MethodHandles支持JIT优化,而反射调用JIT优化受限 -
多次调用后,
MethodHandles优势更加明显
java
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.lang.reflect.Field;
public class FVTest {
private static int i = 1;
static VarHandle v;
static Field f;
static {
try {
v = MethodHandles.lookup().findStaticVarHandle(FVTest.class, "i", int.class);
// 创建更快
f = FVTest.class.getDeclaredField("i");
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
//性能测试
static void main() throws Throwable{
//预热
v.set(1);
v.get();
f.set(null,2);
f.get(null);
//测试
long s1 = System.nanoTime();
for (int i1 = 0; i1 < 100000; i1++) {
v.set(100000);
v.get();
}
long s2 = System.nanoTime();
for (int i1 = 0; i1 < 100000; i1++) {
f.set(null,100);
f.get(null);
}
long s3 = System.nanoTime();
System.out.println("VarHandle: "+(s2-s1)/100000); // 60
System.out.println("Field: "+(s3-s2)/100000); // 128
//预热后在10w次循环下,MethodHandles比Field快2倍多
}
}
六、实际应用场景
6.1 高性能计数器
java
// 高性能无锁计数器
public class HighPerformanceCounter {
private volatile long[] paddedCounter = new long[16];
private static final VarHandle COUNTER_HANDLE;
private static final int PADDING = 8; // 避免伪共享
public long getAndIncrement() {
long current, next;
do {
current = COUNTER_HANDLE.getVolatile(this, 0L);
next = current + 1;
} while (!COUNTER_HANDLE.compareAndSet(this, current, next));
return current;
}
}
6.2 自定义并发数据结构
java
// 简单无锁栈实现
public class LockFreeStack<T> {
private static class Node<T> {
final T item;
volatile Node<T> next;
Node(T item) {
this.item = item;
}
}
private volatile Node<T> top = null;
private static final VarHandle TOP_HANDLE;
public void push(T item) {
Node<T> newNode = new Node<>(item);
Node<T> oldTop;
do {
oldTop = top;
newNode.next = oldTop;
} while (!TOP_HANDLE.compareAndSet(this, oldTop, newNode));
}
public T pop() {
Node<T> oldTop, newTop;
do {
oldTop = top;
if (oldTop == null) return null;
newTop = oldTop.next;
} while (!TOP_HANDLE.compareAndSet(this, oldTop, newTop));
return oldTop.item;
}
}
6.3 状态机管理
java
// 原子状态转换
public class StateMachine {
private static final int STATE_INIT = 0;
private static final int STATE_RUNNING = 1;
private static final int STATE_COMPLETED = 2;
private volatile int state = STATE_INIT;
private static final VarHandle STATE_HANDLE;
public boolean transitionToRunning() {
int current;
do {
current = STATE_HANDLE.getVolatile(this);
if (current != STATE_INIT) {
return false; // 状态不符合预期
}
} while (!STATE_HANDLE.compareAndSet(
this,
STATE_INIT,
STATE_RUNNING
));
return true;
}
}
七、最佳实践与注意事项
7.1 使用建议
-
避免过度使用:只有在确实需要性能优化的关键路径使用
-
保持简单:复杂的VarHandle操作容易出错,保持逻辑简单
-
充分测试:并发场景需要充分的测试验证
-
文档化:为使用VarHandle的代码添加详细注释
7.2 常见陷阱
java
// 陷阱1:忘记内存屏障
// ❌ 错误示例
void unsafeIncrement() {
int value = countHandle.get(this); // 普通读,无内存屏障
countHandle.set(this, value + 1); // 普通写,无内存屏障
}
// ✅ 正确示例
void safeIncrement() {
int current, next;
do {
current = countHandle.getVolatile(this); // volatile读
next = current + 1;
} while (!countHandle.compareAndSet(this, current, next));
}
// 陷阱2:类型不匹配
// ❌ 编译时会报错
// longHandle.set(obj, "string"); // 类型不匹配
7.3 性能优化技巧
-
批量操作:对于频繁的操作,考虑批量处理
-
局部变量:将VarHandle引用存储在局部变量中
-
避免不必要的volatile:只有在需要时使用volatile语义
-
缓存计算结果:对于复杂的计算,考虑缓存结果
八、VarHandle在JDK中的使用
Java自身的并发工具类已经在使用VarHandle进行优化:
8.1 ConcurrentHashMap中的使用
JDK 11+的ConcurrentHashMap内部大量使用VarHandle来替代Unsafe操作,提高了性能和可移植性。
8.2 CompletableFuture中的使用
CompletableFuture使用VarHandle来管理异步操作的多个阶段。
九、高级主题
9.1 VarHandle与JVM内部
VarHandle在JVM内部作为intrinsic方法实现,意味着它们可以被JVM直接识别并优化为本地机器指令,而不是普通的Java方法调用。
9.2 VarHandle与JSR 376(模块系统)
VarHandle的设计考虑到了模块系统的封装性,可以通过module-info.java控制访问权限。
9.3 未来发展趋势
随着Project Valhalla(值类型)和Project Panama(原生内存访问)的推进,VarHandle将扮演更重要的角色。
十、总结
10.1 VarHandle的优势
-
高性能:直接内存访问,减少不必要的开销
-
安全性:比Unsafe更安全,类型检查更严格
-
灵活性:丰富的访问模式,细粒度的内存控制
-
标准化:成为Java标准,更好的平台兼容性
10.2 适用场景
-
需要极致性能的并发数据结构
-
内存敏感的应用
-
需要精细内存控制的特殊场景
-
替代Unsafe的现代化代码
附录:快速参考
A. 创建VarHandle的方法
java
// 实例字段(同一模块内)
MethodHandles.lookup().findVarHandle(class, fieldName, type)
// 实例字段(跨模块访问私有字段)
MethodHandles.privateLookupIn(targetClass, lookup).findVarHandle(class, fieldName, type)
// 静态字段
MethodHandles.lookup().findStaticVarHandle(class, fieldName, type)
// 数组元素
MethodHandles.arrayElementVarHandle(arrayClass)
B. MethodHandles查找方法
java
// 查找实例方法
lookup.findVirtual(class, methodName, methodType)
// 查找静态方法
lookup.findStatic(class, methodName, methodType)
// 查找构造函数
lookup.findConstructor(class, methodType)
// 查找特殊方法
lookup.findSpecial(class, methodName, methodType, specialCallerClass)
C. MethodType创建示例
java
// 无参数返回void
MethodType.methodType(void.class)
// String method(int, double)
MethodType.methodType(String.class, int.class, double.class)
// 构造函数:Object(String)
MethodType.methodType(void.class, String.class)
D. 常用访问模式
-
get()/set(): 普通读写 -
getVolatile()/setVolatile(): volatile读写 -
compareAndSet(): CAS操作 -
getAndSet(): 原子替换 -
getAndAdd(): 原子加法
E. 重要注意事项
-
VarHandle是不可变的
-
VarHandle是线程安全的
-
创建VarHandle相对昂贵,应该静态初始化
- 如果必须动态的则还是推荐
java.lang.reflectAPI,它的创建更快
- 如果必须动态的则还是推荐
-
VarHandle操作是编译器优化友好的