compareAndSet()与weakCompareAndSet()核心行为一致,均通过CAS原子更新变量;差异在于内存语义:前者具全序性并插入内存屏障,后者可能虚假失败且无顺序保证,适用于高竞争、可重试场景。

两者核心行为一致:都尝试用 CAS(Compare-And-Swap)原子地将变量从 expect 更新为 newValue,仅当当前值等于 expect 时才成功,返回 true;否则不修改、返回 false。真正差异不在“做不做”,而在“做了之后怎么同步”以及“没做成时能不能赖——不是失败,是可能假装失败”。
内存语义强度不同
compareAndSet() 是一个全序(sequentially consistent)操作:它带有获取(acquire)和释放(release)语义,会插入内存屏障,确保其前后的读写不会被重排序,并对其他线程可见。这带来确定性,但也附带一定开销。
weakCompareAndSet() 不提供顺序保证,文档明确标注:“May fail spuriously and does not provide ordering guarantees”(可能凭空失败,且不保证内存顺序)。它通常只具备释放语义(release-only)或更弱的语义,在某些平台(如 ARM 或旧版 x86)上可编译为更轻量的指令(例如不带 lock 前缀的 cmpxchg 变体),省去全局总线锁或缓存一致性广播。
spurious failure(虚假失败)的实际含义
虚假失败 ≠ 随机乱返回 false。它指:即使当前值确实等于 expect,weakCompareAndSet() 仍可能返回 false —— 不是因为数据被改了,而是因为底层硬件/VM 的优化策略(比如缓存行失效、TLB 冲突、推测执行回滚等)导致该次 CAS 指令被中止。
- 这不是 bug,是设计契约的一部分
- 它绝不会在值不等于 expect 时返回 true(安全性有保障)
- 但它可能在值等于 expect 时返回 false(可用性略降)
- 因此必须配合循环重试使用,不能当作一次性的条件判断
适用场景:哪些地方真能靠它提性能?
弱 CAS 的价值只在高竞争 + 高吞吐 + 可容忍短暂延迟或重试的场景下显现,典型例子:
- 无锁队列(如
ConcurrentLinkedQueue)的入队/出队头尾指针更新 - 高性能计数器(如指标埋点中的原子累加)的非严格有序更新
- 对象池中资源状态标记(如
FREE → ALLOCATING)的乐观尝试 - 某些 JVM 内部实现(如
Unsafe.compareAndSetInt在特定 CPU 上的 fast-path 分支)
注意:普通业务代码中极少需要主动选用 weakCompareAndSet。JDK 9+ 已开始用 weakCompareAndSetVolatile 替代旧版,语义更清晰;而 JDK 17+ 中,多数 weakCompareAndSet 方法已标记为 @Deprecated(forRemoval = true),推荐迁移到带明确内存模式的方法(如 weakCompareAndSetPlain / weakCompareAndSetAcquire)。
硬件依赖与实测建议
弱 CAS 的收益高度依赖底层架构:
- x86/x64:差异极小,
cmpxchg本身已足够高效,weak 版本几乎无优势 - ARM64:弱 CAS 可映射为
ldaxr/stlxr对,相比ldar/stlr(对应 full CAS)减少内存屏障强度,实测在 16 核以上争抢场景下吞吐可提升 5%–12% - 某些 RISC-V 实现:weak 版本可跳过部分 cache coherency handshake,降低延迟抖动
若真要验证效果,应使用 JMH 在目标机器上对比:
- 同一逻辑分别用
compareAndSet和weakCompareAndSet循环实现 - 固定线程数(≥CPU 核心数)、高争抢(多线程反复更新同一变量)
- ops/ms 和
gc.count,而非单次成功率


















