原子操作

220 阅读2分钟

主要就是CAS与锁


一、CAS(无锁机制)

1. 核心思想

通过 硬件指令(如 CPU 的 cmpxchg 实现原子操作,无需线程阻塞,直接在用户态完成竞争。

2. 特点

  • 优点

    • 无锁,高并发下吞吐量高。
    • 避免线程上下文切换和阻塞开销。
  • 缺点

    • ABA 问题:值从 A → B → A,CAS 无法感知中间变化(可通过 AtomicStampedReference 解决)。
    • 自旋开销:高竞争时 CAS 失败率高,CPU 空转。

3. 应用场景

  • 简单原子操作(如计数器增减)。
  • 低竞争环境(如线程交替修改共享变量)。
  • Java 中的 AtomicXXX 类、LongAdder 等均基于 CAS。

二、锁(Locking)

1. 核心思想

通过 互斥(Mutex) 强制代码块串行执行,保证原子性,依赖操作系统或 JVM 的线程调度。

2. 特点

  • 优点

    • 严格保证原子性,适合复杂操作(如先读后写)。
    • 解决 CAS 无法处理的复合操作问题。
  • 缺点

    • 线程阻塞和唤醒涉及内核态切换,性能开销大。
    • 可能导致死锁、优先级反转等问题。

3. 应用场景

  • 高竞争环境(如多线程频繁争抢同一资源)。
  • 复合操作(如银行转账:先扣减 A,再增加 B)。
  • Java 中的 synchronizedReentrantLock 均基于锁。

三、CAS 与锁的对比

维度CAS
实现方式硬件指令(用户态)操作系统互斥量(内核态)
竞争处理自旋重试线程阻塞等待
适用场景低竞争、简单操作高竞争、复杂操作
性能开销低(无阻塞)高(上下文切换)
典型问题ABA 问题死锁、饥饿

四、其他辅助机制

虽然 CAS 和锁是核心,但 Java 还提供了一些高级工具来优化原子操作:

  1. volatile 关键字

    • 保证可见性和有序性,但不保证原子性。
    • 通常与 CAS 结合使用(如 AtomicInteger 内部依赖 volatile 变量)。
  2. LongAdder 分段锁

    • 通过分散竞争(类似 ConcurrentHashMap 的分段锁)减少 CAS 冲突。
    • 高并发下性能优于 AtomicLong
  3. VarHandle(Java 9+)

    • 提供更灵活的内存操作 API(如原子字段更新),替代 Unsafe 类。

五、如何选择?

  1. 优先 CAS: 简单操作(如计数器)且竞争不激烈时,用 AtomicXXX 类。
  2. 慎用锁: 复杂操作或高竞争时,用 synchronizedReentrantLock
  3. 特定优化: 高并发统计场景用 LongAdder,复合操作考虑锁或 StampedLock

总结

Java 原子操作的底层本质确实是 CAS,但两者并非对立关系:

  • CAS 是乐观锁(假设无冲突,失败重试)。
  • 锁是悲观锁(假设有冲突,直接互斥)。

实际开发中需根据场景权衡,甚至混合使用(如 ConcurrentHashMap 分段锁 + CAS)。