首页 > 项目 > 当前页面

Atomic 原子类如何保证原子性?

2026-06-13 NEW个对象

📌 Atomic 原子类如何保证原子性?

核心结论:Atomic 类通过 CAS(Compare And Swap)+ CPU 原语 + 内存可见性控制,实现无锁条件下的原子性操作,从而避免传统 synchronized 的重量级锁开销。

1️⃣ 问题背景

在高并发场景中,如果多个线程同时修改一个变量,就会出现线程安全问题,例如:

  • 计数器递增丢失更新
  • 库存扣减错误
  • 状态更新覆盖

传统解决方案是使用 synchronizedLock,但这些方式属于悲观锁机制,性能开销较大。

因此 Java 提供了 Atomic 原子类,用于在低锁甚至无锁情况下实现线程安全。


2️⃣ 核心原理

Atomic 类的核心依赖是 CAS(Compare And Swap)。

CAS = 比较 + 交换 + 失败重试机制

CAS 包含三个核心参数:

  • 内存值 V
  • 预期值 A
  • 更新值 B

执行逻辑如下:

如果 V == A:则 V = B
否则:不做任何操作,重新尝试

Atomic 类底层依赖 Unsafe 类调用 CPU 指令(如 cmpxchg),实现硬件级别原子性


3️⃣ 数据结构分析

AtomicInteger 为例,其核心结构如下:

  • value:volatile 修饰的 int
  • Unsafe:用于底层 CAS 操作
  • valueOffset:内存偏移量
private volatile int value;
private static final Unsafe unsafe;
private static final long valueOffset;

关键点:

volatile + CAS = 可见性 + 原子性基础

4️⃣ 算法分析

Atomic 操作本质是一个自旋 + CAS 重试算法

1. 读取当前值 oldValue
2. 计算新值 newValue
3. CAS 尝试更新
4. 如果失败则重新循环

该算法属于乐观锁思想,即默认不会发生冲突,只有冲突时才重试。

优点是避免线程阻塞,但缺点是高竞争环境下会导致CPU 自旋浪费


5️⃣ 执行流程

AtomicInteger.incrementAndGet()

for (;;) {
  int current = get();
  int next = current + 1;
  if (compareAndSet(current, next))
    return next;
}

流程说明:

读取值 → 计算 → CAS尝试 → 成功返回 / 失败重试

核心是无限循环 + CAS 保证最终只有一个线程成功更新。


6️⃣ 实际案例

多线程计数器

AtomicInteger count = new AtomicInteger(0);

Runnable task = () -> {
  for (int i = 0; i < 1000; i++) {
    count.incrementAndGet();
  }
};

启动 100 个线程,每个线程执行 1000 次:

最终结果一定是 100000(保证原子性)

如果使用普通 int:

结果会小于 100000(线程覆盖导致丢失更新)

7️⃣ 优缺点分析

优点

  • 无锁实现,高性能
  • 基于 CPU 指令,执行效率高
  • 避免线程阻塞
  • 适合低冲突场景

缺点

  • 高并发下 CAS 自旋浪费 CPU
  • ABA 问题(需要版本号解决)
  • 只能保证单变量原子性

8️⃣ 面试常见问题

1. Atomic 为什么是线程安全的?

因为底层依赖 CAS + volatile + CPU 原子指令。

2. CAS 有什么缺点?

ABA 问题 + 自旋消耗 CPU。

3. Atomic 和 synchronized 区别?

对比项 Atomic synchronized
实现方式 CAS 锁机制
性能 较低
阻塞 非阻塞 阻塞

4. 如何解决 ABA 问题?

使用 AtomicStampedReference(版本号机制)。


9️⃣ 总结

Atomic 原子类的本质:

① 利用 CAS 实现无锁竞争

② 依赖 CPU 指令保证原子性

③ 结合 volatile 保证可见性

核心思想:

乐观锁 + 自旋重试 + 硬件级别原子指令

相关文章

NEW个对象 NEW个对象
JAVA是世界上最好的语言