VarHandle比AtomicFieldUpdater更底层,因其在创建时完成字段绑定与权限校验,运行时直接映射JVM字节码语义,无反射开销;而AtomicFieldUpdater基于反射+Unsafe,每次调用需动态校验与类型检查。

VarHandle 为什么比 AtomicFieldUpdater 更底层
因为 AtomicFieldUpdater 是基于反射 + Unsafe 封装的运行时工具类,每次调用都要做字段可访问性检查、类型校验、甚至动态生成字节码(JDK 9+ 后部分路径已优化),而 VarHandle 在创建阶段就完成字段绑定与权限校验,运行时只做轻量级内存语义控制,直接映射到 JVM 的 getfield/putfield 字节码语义,没有反射链路开销。
创建私有字段的 VarHandle 必须绕过 IllegalAccessException
直接用 MethodHandles.lookup() 查 private 字段会抛 IllegalAccessException——这是最常卡住的地方。正确做法是用 MethodHandles.privateLookupIn() 获取带私有访问权限的 lookup 实例:
MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(TargetClass.class, MethodHandles.lookup())- 再调用
lookup.findVarHandle(TargetClass.class, "fieldName", int.class) - 不能复用其他类的 lookup 实例,哪怕只是父子类关系也不行
- 如果目标类在模块中未开放包(如未声明
opens com.example to java.base),仍会失败
compareAndSet 和 weakCompareAndSetPlain 的语义差异直接影响重试逻辑
名字带 weak 的方法(如 weakCompareAndSetPlain)允许“假失败”(spurious failure):即使当前值匹配,也可能返回 false。它不提供 happens-before 保证,但 ARM 上无额外屏障,x86 上也省去部分指令。而 compareAndSet 提供强顺序保证,对应硬件级 cmpxchg 指令。
- 需要严格线程可见性的场景(如状态机跃迁),必须用
compareAndSet - 高频自旋更新且能容忍偶尔重试(如计数器累加),可用
weakCompareAndSetPlain省点 cycle - 别把
weakCompareAndSetPlain当成性能银弹——它不保证成功,循环里必须保留重试结构
内存访问模式选错会导致可见性 bug
VarHandle 默认的 get()/set() 是 volatile 语义,但你也可以显式指定更细粒度的屏障,比如 getAcquire() 或 setRelease()。错配会破坏同步契约:
- 用
setOpaque()写入后,另一个线程用get()读可能看不到最新值(无 happens-before) - 在无锁队列的 tail 更新中,常用
setRelease()配合getAcquire(),避免编译器/JIT 重排序破坏链表结构 - 不要为了“看起来快”就默认切到
Opaque,除非你明确画出了内存模型图并验证过
真正难的不是写出第一个 VarHandle,而是确认每个 get/set 调用背后的内存屏障是否和你的并发协议对齐——这没法靠测试覆盖,得靠对 JSR-133 和目标 CPU 架构的理解。

















