☰
三、CAS 与 Atomic
2026/10/15 17:06:50 网站建设 项目流程

├── 三、CAS 与 Atomic


│ ├── CAS 原理

比较内存中的值是否还是自己读取时的值,如果是,就修改;否则失败,再重新尝试。

a(希望的值,修改的值)

建议 内存当前的值是否是希望的值

是就修改

CAS 的"比较 + 修改"并不是两步操作,而是CPU 提供的一条原子指令(如 x86 的CMPXCHG)完成的。

参数含义举例
V内存中的真实值value=5
A期望值(Expected)线程读取到5
B新值(New)修改成6

线程读取 value=5
│
▼
准备修改为6
│
▼
CPU执行CAS
│
▼
value还是5?
│
┌─┴────────┐
│ │
是 否
│ │
修改成功 修改失败
│ │
结束 重新读取

CAS 为什么线程安全

CAS 是**乐观锁(Optimistic Lock)**思想的典型实现,认为大多数情况下不会发生冲突,冲突时再重试。

CAS 底层原理

AtomicInteger.incrementAndGet();

Unsafe.compareAndSwapInt()

VarHandle.compareAndSet()

CPU 原子指令 CMPXCHG(x86)

AtomicInteger
│
▼
Unsafe / VarHandle
│
▼
JNI
│
▼
CPU CAS 指令
│
▼
修改内存

自旋

优点缺点
无锁,性能高高竞争时不断重试,浪费 CPU
不会发生线程阻塞存在 ABA 问题
CPU 原子指令保证安全只能保证单个共享变量的原子性
高并发效率高多变量需要额外机制(如AtomicReference、锁等)
│ ├── ABA 问题

ABA 问题是 CAS 的经典缺陷。

CAS 只判断"当前值是否等于期望值",并不知道这个值在中间是否被别人修改过。

A
↓

B

↓

A

值虽然又变回了A,但实际上已经发生过变化。

CAS 看到还是A,会误以为"没人修改过",于是修改成功。

解决方式

版本号

value=A version=1

虽然值一样,

但是版本已经变化。

CAS 能发现数据被修改过。

JDK 提供:

  • AtomicStampedReference(值 + 版本号)
  • AtomicMarkableReference(值 + 标记位)

其中最常用的是:

AtomicStampedReference:每次更新同时比较引用和值对应的版本号(Stamp),只要版本号变化,即使值恢复原样,CAS 也会失败。

ABA 问题是 CAS 的典型缺陷。由于 CAS 只比较当前值是否等于期望值,而不会检查该值在中间是否发生过变化,因此当数据经历 A→B→A 后,CAS 仍会认为数据未被修改。Java 通常通过AtomicStampedReference引入版本号(Stamp),将比较对象从"值"扩展为"值 + 版本号",从而避免 ABA 问题。

超级多类


│ ├── AtomicReference
│ ├── AtomicStampedReference
│ ├── AtomicMarkableReference
│ ├── AtomicIntegerFieldUpdater
│ └── Unsafe 类

类操作对象主要作用解决什么问题是否解决 ABA典型场景
AtomicReference<T>引用对象原子更新对象引用普通 CAS 只能更新基本类型,不能方便地原子更新对象引用❌无锁链表、无锁队列、缓存对象切换
AtomicStampedReference<T>引用 + 版本号在 CAS 的基础上增加版本号(Stamp)解决 ABA 问题✅链表、栈、队列等需要检测引用是否被修改过
AtomicMarkableReference<T>引用 + 标记位给对象增加一个布尔标记(true/false)标记对象状态,而不是记录修改次数⚠️ 部分避免(不能完全解决)逻辑删除、是否已处理、是否失效
AtomicIntegerFieldUpdater<T>对象中的int字段不创建AtomicInteger,直接原子更新对象字段减少内存占用❌大量对象中的计数器、状态字段
Unsafe任意内存提供底层 CAS、内存操作、对象创建等能力JVM 底层实现并发工具取决于使用方式JUC、Netty、Spring、JVM 底层
可见性

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询