CAS (Compare-and-Swap) 本身并不是一个独立的项目或软件,而是一种底层的硬件指令和并发编程概念
1. 核心概念
- CAS 是一种原子操作: 它的 “比较” 和 “交换” 这两个动作是作为一个不可分割的整体执行的,要么都成功,要么都失败,不会出现中间状态。
- CAS 是一种无锁操作(乐观锁): 它在操作过程中不会阻塞线程,而是通过不断重试来实现同步。
- CAS 操作三个数:
- 内存位置 (V): 要读取和修改的内存地址。
- 预期原值 (A): 期望 V 处当前的值。
- 新值 (B): 如果 V 处的值等于 A,则将 V 处的值更新为 B。
2. CPU 指令层面 (以 x86 架构为例)
CAS 的原子性是由 CPU 的硬件指令保证的。 在 x86 架构上,CAS 操作通常对应 cmpxchg 指令族(Compare and Exchange)。 不同的操作数类型对应不同的指令:
cmpxchg(操作数可以是 8 位、16 位、32 位或 64 位)cmpxchg8b(操作数是 64 位)cmpxchg16b(操作数是 128 位,需要 CPU 支持)
cmpxchg 指令的工作流程(以 32 位为例):
-
准备阶段:
- 将预期原值 (A) 放入 EAX 寄存器。
- 将新值 (B) 放入 EBX 或 ECX 寄存器(具体取决于指令的操作数)。
- 将内存位置 (V) 的地址放入一个通用寄存器(例如,ESI 或 EDI)。
-
执行
cmpxchg指令:- CPU 执行
cmpxchg指令,格式通常是:cmpxchg 目标操作数, 源操作数。 - 目标操作数通常是一个内存地址(例如
[ESI],表示 ESI 寄存器中存储的地址)。 - 源操作数通常是一个寄存器(例如
EBX)。
- CPU 执行
-
比较与交换:
cmpxchg指令内部会进行以下操作(原子操作):- 读取内存位置 (V) 的当前值(假设为 C)。
- 将 C 与 EAX 寄存器中的值 (A) 进行比较。
- 如果 C 等于 A:
- 将 EBX 寄存器中的值 (B) 写入内存位置 (V)。
- 将零标志位 (ZF) 设置为 1,表示比较相等。
- 如果 C 不等于 A:
- 将内存位置 (V) 的值 © 加载到 EAX 寄存器中。
- 将零标志位 (ZF) 设置为 0,表示比较不相等。
-
结果判断:
cmpxchg指令执行完成后,程序可以通过检查零标志位 (ZF) 来判断 CAS 操作是否成功。- 如果 ZF = 1,表示 CAS 操作成功,内存位置 (V) 的值已经被更新为 B。
- 如果 ZF = 0,表示 CAS 操作失败,内存位置 (V) 的值没有被修改,EAX 寄存器中现在存储的是 V 处的当前值。
关键点:
- 原子性:
cmpxchg指令本身是原子的,CPU 会保证这个指令在执行过程中不会被中断。 - 总线锁或缓存锁: 在多核处理器环境下,为了保证
cmpxchg指令的原子性,CPU 会使用总线锁 (BUS Lock) 或缓存锁 (Cache Lock) 机制。- 总线锁: 在早期 CPU 中,
cmpxchg指令会在总线上发送一个 LOCK# 信号,锁定总线,阻止其他 CPU 访问内存,从而保证操作的原子性。 总线锁的开销较大。 - 缓存锁: 现代 CPU 更多地使用缓存锁机制。 如果要操作的内存区域在 CPU 的缓存行 (Cache Line) 中,CPU 会锁定该缓存行,而不是锁定整个总线。 缓存锁的开销较小,可以提高多核处理器的性能。
- 总线锁: 在早期 CPU 中,
- EAX 寄存器: EAX 寄存器在
cmpxchg指令中扮演着重要的角色,它既用于存储预期原值 (A),也用于在 CAS 操作失败时存储内存位置 (V) 的当前值。
3. Java 层面 (Unsafe 类)
Java 无法直接使用 CPU 指令,但可以通过 sun.misc.Unsafe 类间接使用。 Unsafe 类提供了一些 native 方法,可以调用底层的操作系统和硬件功能,包括 CAS 操作。
-
Unsafe类中的 CAS 方法:public final native boolean compareAndSwapObject(Obj

最低0.47元/天 解锁文章
724

被折叠的 条评论
为什么被折叠?



