VarHandle 是 JDK 23 中唯一官方推荐替代 Unsafe 的字段级原子操作机制,必须用于生产并发;构造时需按字段访问权限选用 lookup 或 privateLookupIn,类型、内存语义、缓存策略等四维度须严格对齐。

VarHandle 是现代 JDK 中唯一被官方明确推荐、且已在 JDK 23 完成对 Unsafe 字段级原子操作实质性替代的机制——它不是“可选方案”,而是生产级并发中必须迁入的基础设施。
构造 VarHandle 时权限不匹配直接抛 IllegalAccessException
这不是配置问题,是 lookup 实例根本没拿到访问权。常见于想读 private 字段却只用 MethodHandles.lookup()。
- public/protected 字段:用
MethodHandles.lookup().in(Owner.class).findVarHandle(...) - private 字段:必须用
MethodHandles.privateLookupIn(Owner.class, MethodHandles.lookup()),否则必抛异常 - 静态字段:别混用
findVarHandle,该用findStaticVarHandle - 泛型类字段(如
List<String> field):类型参数被擦除,findVarHandle第三个参数必须传Object.class,不能传List.class
compareAndSet 在 final 字段或非 volatile 字段上静默退化或直接报错
VarHandle 对字段内存语义有硬性要求:JDK 14+ 允许非 volatile 实例字段,但 final 字段仍禁止 compareAndSet;static final 字段则完全不可写。
- final 实例字段:允许
getVolatile(),但调compareAndSet()会立即抛UnsupportedOperationException - 非 volatile 实例字段:JDK 14+ 支持,但
compareAndSet可能退化为普通赋值(无屏障),需靠getVolatile/setVolatile显式保障可见性 - 数组元素更新:必须用
VarHandle.ofArray(int[].class),不能用对象字段句柄去操作数组索引
高频调用场景下不缓存 VarHandle 实例,性能直接掉 20–50 倍
每次调 findVarHandle 都要解析签名、校验权限、生成适配字节码,开销远高于其方法调用本身。
- 静态字段:声明为
private static final VarHandle VH_FIELD,在static块中初始化 - 实例字段:若需动态绑定(如泛型容器),至少按类缓存,避免每 new 一个对象就重建句柄
- 别用
MethodHandles.publicLookup()—— 它连 public 字段都可能因模块隔离失败,更别说 private
内存语义选错导致线程间状态不可见或重排序
默认 get()/set() 是 volatile 语义,但你可能只需要 acquire/release 或更弱的 plain 模式。选错就等于白用 VarHandle。
- 状态机跃迁(如从 RUNNING → STOPPED):必须用
compareAndSet()或setRelease()+getAcquire()成对使用 - 计数器累加:用
getAndAdd(),它自带 volatile 写语义,无需额外屏障 - 纯本地暂存(如线程内预计算):可用
getPlain()/setPlain(),无内存屏障,最快 - weakCompareAndSet:适合自旋重试,但 ARM 上可能假失败,关键状态变更禁用
最易被忽略的是字段类型必须与声明类型完全一致:写 int.class 查 Integer 字段?运行时报错;查 volatile int value 却传 Integer.class?一样挂。类型擦除、装箱、访问修饰符、内存语义——四个维度全得对齐,缺一不可。

















