varhandle 的 plain、opaque 和 acquire/release 是三种不同强度的内存访问语义:plain 无同步保障,仅适用于单线程;opaque 保证原子性与最终可见性,但不禁止重排序;acquire/release 通过禁止重排序建立 happens-before 关系,用于精确同步。

VarHandle 的 Plain、Opaque 和 Acquire/Release 是三种不同强度的内存访问语义,对应着对可见性、原子性与重排序的不同约束。选错模式可能导致线程间读不到最新值、逻辑乱序甚至死循环——不是性能问题,而是正确性问题。
Plain:最基础的读写,不保证多线程安全
Plain 模式等价于普通字段访问(如 obj.field = 1 或 int x = obj.field),不做任何内存屏障插入。
- 读写操作可能被 JIT 编译器或 CPU 重排序
- 一个线程写入后,另一个线程可能永远看不到该值(缓存未刷新、指令重排、编译优化)
- 适用于单线程场景,或明确知道无需跨线程同步的临时变量
Opaque:保证原子性与本线程可见,但不约束重排序
Opaque 提供“coherent”语义:只要其他线程写过,当前线程最终一定能读到那个新值(即不会永远卡在旧值上),且对 long/double 等 64 位类型也保证原子读写。
- 解决了 Plain 的“永远读不到更新”的问题(比如 while(x == 0) 死循环)
- 但不禁止编译器/CPU 将它前后的读写操作重排 —— 所以不能用于构建锁、信号量等依赖顺序的结构
- 适合用作状态标志位(如
isReady),仅需“最终可见”,不要求操作前后顺序严格
Acquire/Release:建立同步关系,控制指令重排序边界
Acquire(读)和 Release(写)是一对配合使用的语义,常用于无锁编程中的“发布-消费”模式。它们本身不提供全局可见性保证,但通过禁止重排序来建立 happens-before 关系。
-
getAcquire():确保该读操作之后的所有读/写不会被重排到它前面;若读到了某次setRelease()写入的值,则能保证看到该写入之前的所有操作结果 -
setRelease():确保该写操作之前的所有读/写不会被重排到它后面;为后续的getAcquire()提供“发布”效果 - 典型用法:线程 A 初始化数据 →
setRelease(flag, 1);线程 Bif (getAcquire(flag) == 1)成立 → 就能安全读取已初始化的数据
这三类语义不是互斥升级,而是按需选用:Plain 最轻量但最危险,Opaque 解决基本可见性,Acquire/Release 构建精确同步点。volatile 语义(getVolatile/setVolatile)则比 Acquire/Release 更强——它同时具备 acquire + release 效果,还强制全局可见,但开销略高。










