atomicinteger、atomicreference 等仅对单次cas操作(如incrementandget、compareandset)真正无锁;多字段联动、复杂逻辑或误用++/==等仍需锁,且高竞争下性能未必优于synchronized。

Java 的 AtomicInteger、AtomicReference 等类不是“万能无锁替代品”,它们只在特定场景下真正避免锁,用错反而掩盖竞态或引发逻辑错误。
哪些操作能真正无锁执行
底层依赖 CPU 的 CAS(Compare-And-Swap)指令,所以只有满足“读–改–写”原子性且不依赖全局状态的操作才安全:
-
incrementAndGet()、compareAndSet(expected, updated)这类单次 CAS 操作是真正无锁的 -
getAndIncrement()本质也是 CAS 循环,但返回旧值,仍属无锁 -
updateAndGet(UnaryOperator)在 Java 8+ 支持,但内部仍是循环 CAS,适用于简单纯函数逻辑 - 涉及多个字段联动更新(如账户余额 + 日志版本号)无法靠单个
AtomicInteger保证原子性,必须用锁或AtomicReference包装整个对象
为什么直接用 ++ 或 += 不行
因为 i++ 是三步:读取 i → 计算 i+1 → 写回 i。即使变量是 AtomicInteger,写成 ai++ 会触发自动拆箱为 int,实际调用的是普通 int 的自增,完全绕过原子逻辑。
正确写法只能是:
立即学习“Java免费学习笔记(深入)”;
ai.incrementAndGet(); // ✅ ai.addAndGet(1); // ✅ ai.set(ai.get() + 1); // ❌ 表面看像原子,实则是 get + set 两步,中间可能被其他线程修改
AtomicReference 误用最常见于对象引用比较
它默认用 == 判断引用是否相等,不是 equals()。如果用它管理一个自定义对象(比如 User),compareAndSet(oldUser, newUser) 失败往往是因为你传入了内容相同但非同一实例的 oldUser。
- 确保
oldUser是之前get()返回的那个引用,不要 new 一个新对象去比 - 若需按业务字段判断相等,应把关键字段(如 id + version)封装进一个不可变容器类,再用
AtomicReference管理该容器 -
weakCompareAndSet()在某些 JVM 上可能失败却不抛异常,生产环境一律用compareAndSet()
性能不总比 synchronized 好
CAS 在低竞争时快,但高竞争时会不断重试,导致 CPU 空转;而 synchronized 在 Java 6+ 后有锁消除、偏向锁、轻量级锁优化,实际开销可能更低。
- 简单计数器、开关标志位(
AtomicBoolean)适合用原子类 - 一段含多步校验、IO 或复杂分支的逻辑,强行拆成多个
compareAndSet()反而更慢、更难维护 - JVM 参数
-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly可查看是否真生成了lock cmpxchg指令,否则可能退化为锁
真正难的不是调用哪个方法,而是判断“当前业务变更是否可被表达为一次 CAS”。多数时候,你以为的无锁,只是把锁藏在了重试逻辑里。










