lazyset是atomicinteger等原子类中一种轻量级写操作,它仅插入storestore屏障、不触发storeload屏障与缓存刷新,牺牲即时可见性换取极致写性能;适用于单写多读且容忍延迟的场景,如无锁队列tail更新、状态标志位切换等。

lazySet 是原子类中一种轻量级写操作,它不强制刷新写缓冲区(Store Buffer),也不插入 StoreLoad 内存屏障,只保留 StoreStore 屏障。这意味着:值一定会被写入,但其他线程读到新值的时间不可控——不是“会不会看到”,而是“什么时候看到”。它牺牲即时可见性,换取写路径的极致性能。
它到底绕过了什么?
对比 set():
-
set()等价于 volatile 写:触发StoreStore + StoreLoad屏障,确保该写之前所有操作完成、之后所有读操作能看到最新值; -
lazySet()只插入StoreStore屏障:禁止该写操作被重排序到前面的读/写之后,但不阻止后续读操作从旧缓存行取值,也不强制刷 Store Buffer 到主内存。
底层是怎么落地的?
在主流 JVM(如 HotSpot)中,lazySet 最终调用 Unsafe.putOrderedXXX 方法:
- x86 平台通常编译为普通
mov指令(非lock xchg或mfence),无总线锁定开销; - ARM/Aarch64 上对应
stlr(store-release),语义上仅保证顺序性,不触发全局同步; - JIT 编译器会识别该模式并跳过冗余屏障插入,避免 runtime 时额外 fence 开销。
哪些场景敢用 lazySet?
核心判断标准:**写方唯一,读方不依赖“立即感知”——只要最终能读到,程序逻辑就成立。** 典型例子包括:
- 无锁队列(如 MpscQueue)的 tail 指针更新:生产者单线程推进,消费者轮询读取,短暂延迟不影响正确性;
- 状态机标记位(如
AtomicInteger status):从 RUNNING → STOPPING 的切换,后续检查可容忍几纳秒~几微秒延迟; - 监控指标计数器(非精确实时告警):如 QPS 统计桶的翻转标记,只要最终归零即可,无需强同步。
哪些情况绝不能用?
一旦涉及跨线程协作的“信号语义”,lazySet 就会埋下隐患:
- 作为双重检查锁(DCL)中的 volatile 字段替代品:无法建立 happens-before,可能导致对象未完全构造就被其他线程看到;
- 与普通变量混用做同步条件:比如
flag.lazySet(true)后直接读data,不能保证看到data的写入结果; - 要求“写即生效”的关闭开关:如连接池 shutdown 标志,若消费者延迟读到,可能继续分发已失效连接。
不复杂但容易忽略:它不是 bug,也不是弱实现,而是有明确契约的性能原语——用对地方,是百万级 QPS 的关键一环;用错位置,是极难复现的并发幽灵。










