跳转至

AtomicInteger 的 CAS 无锁原理

一、问题

int i = 0;
i++;   // 多线程下不安全

i++ 是读-改-写三步,多线程下会丢数据。用 synchronized 太重。

二、CAS 是什么

Compare And Swap,一条 CPU 指令:

CAS(内存地址, 期望值, 新值)
  if (*addr == 期望值) {
      *addr = 新值;
      return true;
  }
  return false;

整个操作是原子的(CPU 指令保证)。

三、AtomicInteger.incrementAndGet()

public final int incrementAndGet() {
    return U.getAndAddInt(this, VALUE, 1) + 1;
}

U.getAndAddInt 底层:

public final int getAndAddInt(Object o, long offset, int delta) {
    int v;
    do {
        v = getIntVolatile(o, offset);   // 读当前值
    } while (!compareAndSwapInt(o, offset, v, v + delta));
    return v;
}

流程

  1. 读当前值 v。
  2. 计算新值 v + delta。
  3. CAS:如果内存里还是 v,就改成 v + delta。
  4. CAS 失败(被别的线程改了),回到 1 重试(自旋)。

四、为什么无锁

没有 synchronized,没有线程阻塞。竞争激烈时自旋空转,但不用 OS 切换线程,性能更高。

五、适用场景

  • 计数器(点击数、点赞数)。
  • 状态机。
  • 竞争不激烈(竞争激烈时自旋浪费 CPU)。

CAS 的缺点

  1. ABA 问题。
  2. 自旋浪费 CPU。
  3. 只能保证一个变量的原子性。