cas必须先比较再交换,因为多线程下读取的值可能瞬间过期(如aba问题),直接写会覆盖中间变更;它采用乐观锁机制,仅当值未变时才更新,失败需手动处理。

为什么 CAS 不是“直接改值”,而是非要先比较?
因为多线程下,你读到的值可能一拿到手就过期了。比如线程 A 读出 count = 5,打算加 1;但还没写回去时,线程 B 已经把它改成 6 又改回 5(ABA 场景)。如果直接写 count = 6,就覆盖了中间的真实变更——CAS 强制你声明:“我只在它还是 5 的时候才写 6”,否则就失败,由你决定重试或放弃。
- 这是乐观锁逻辑:不抢锁,先信没人动,动了再协商
- 失败不阻塞,但也不自动重试——
AtomicInteger.compareAndSet()返回false,你得自己写循环 - 硬件上靠 CPU 指令(如 x86 的
CMPXCHG)保证“读-比-写”三步不可拆分
compareAndSet 和 getAndIncrement 本质区别在哪?
前者是裸 CAS 原语,后者是封装好的原子操作——但它们都基于同一底层机制。比如 getAndIncrement() 内部就是个自旋循环:do { old = get(); } while (!compareAndSet(old, old + 1));
-
compareAndSet(expected, newValue)是最基础的布尔型判断,适合复杂条件(如“仅当状态为 READY 时才设为 RUNNING”) -
getAndAdd(delta)、updateAndGet(unaryOperator)等方法内部都依赖 CAS 自旋,不是“更高效”,而是“更易用” - 别误以为
incrementAndGet()比手动 CAS 快——它只是帮你把循环写好了;高争用下,自旋本身仍消耗 CPU
ABA 问题不是理论风险,是真实会崩的场景
典型例子:一个 AtomicReference<node></node> 维护链表头节点,线程 A 取出 head=A,被挂起;线程 B 把 A 出队、插入新节点 C、再把 A 插回队首——head 又变回 A,但链表结构已不同。此时 A 的 CAS 成功,却破坏了数据一致性。
- 普通
AtomicInteger遇不到 ABA,但引用类型、指针操作、无锁栈/队列极易中招 - Java 提供
AtomicStampedReference,用int stamp当版本号,CAS 同时校验引用 + 版本 - 注意:stamp 不能只靠自增,要和业务语义绑定(比如每次修改结构,stamp += 1)
别把 CAS 当万能锁替代,它只保单变量原子性
CAS 天然只能保护一个内存地址。你想“扣库存且更新订单状态”,两个字段分别用 AtomicInteger 和 AtomicBoolean,依然可能卡在中间态:库存扣了,订单没改,系统重启就对不上账。
- 多个变量需原子更新?要么上
synchronized或ReentrantLock - 要么打包成对象,用
AtomicReference<orderstate></orderstate>,每次 CAS 整个对象(注意对象不可变性) - 别迷信“无锁一定快”——低并发时,CAS 自旋开销可能高于一次轻量锁获取
compareAndSet() 的返回值里。










