Java 中如何利用 CAS 实现原子操作?以AtomicInteger 为例
在 Java 中,
AtomicInteger
是基于 CAS(Compare-and-Swap,比较并交换)机制实现原子操作的典型案例。它通过sun.misc.Unsafe
类提供的 native 方法直接操作内存地址,确保多线程环境下对整数的修改具有原子性。以下是其实现原理的详细分析:
一、核心字段与 Unsafe 依赖
AtomicInteger
的核心是通过Unsafe
类操作一个volatile int
变量,保证可见性和原子性:
volatile int value
:存储整数的实际值,volatile
确保一个线程修改后,其他线程能立即看到最新值(可见性)。Unsafe
类:Java 提供的 “后门” 工具类,可直接操作内存地址,支持 CAS 等底层操作。valueOffset
:value
字段在对象内存中的偏移量,Unsafe
通过它定位到具体内存地址。
二、AtomicInteger 常用方法
1. public final int get() //获取当前的值
2. public final int getAndSet(int newValue) //获取当前的值,并设置新的值
3. public final int getAndIncrement() //获取当前的值,并自增
4. public final int getAndDecrement() //获取当前的值,并自减
5. public final int getAndAdd(int delta) //获取当前的值,并加上预期的值
6. boolean compareAndSet(int expect, int update) //如果输入的数值等于预期值,则以原子方式将该 值更新为输入值(update)
三、CAS 操作的核心实现:compareAndSet
方法
AtomicInteger
的所有原子操作(如自增、累加)最终都依赖compareAndSet
方法,该方法通过Unsafe
的compareAndSwapInt
实现 CAS:源码如下:
public final boolean compareAndSet(int expect, int update) {return unsafe.compareAndSwapInt(this, valueOffset, expect, update);}参数说明:
this:当前AtomicInteger对象。
valueOffset:value字段的内存偏移量。
expect:预期的当前值。
update:要更新的新值。底层逻辑:
原子性地检查value字段的当前值是否等于expect:
若等于,则将其更新为update,返回true。
若不等于,则不做修改,返回false。
整个过程由 CPU 指令保证原子性,无需加锁。
四、典型原子操作的实现:
AtomicInteger
的其他方法(如自减、累加)均采用类似 “循环 + CAS” 的模式:
decrementAndGet(自减并返回新值):
addAndGet
(累加并返回新值):
五、CAS 机制的优势与局限
优势:
- 无锁开销:无需
synchronized
或锁机制,避免线程阻塞和上下文切换,性能更优(尤其低并发场景)。 - 硬件级原子性:依赖 CPU 的 CAS 指令(如 x86 的
cmpxchg
),操作具有天然原子性。
局限:
- ABA 问题:如前所述,值从 A→B→A 时,CAS 会误判为未修改(可通过
AtomicStampedReference
解决)。 - 自旋开销:高并发下 CAS 失败率高,线程会频繁自旋重试,消耗 CPU 资源。
- 只能保证单个变量的原子性:无法直接实现多个变量的复合原子操作。
总结:
AtomicInteger
通过以下方式实现原子操作:
- 用
volatile int value
存储值,保证可见性。- 依赖
Unsafe
类的compareAndSwapInt
方法,直接操作内存地址实现 CAS。- 所有原子操作(自增、自减等)通过 “循环 + CAS” 模式,确保操作失败时重试,直到成功。