一句话说透Java里面的CAS

252 阅读3分钟

CAS(Compare And Swap)  是一种 无锁并发控制技术,用于实现线程安全的原子操作。它的核心思想是:先比较内存中的值是否与预期一致,若一致则更新,否则重试。以下是其核心要点:


一、CAS 核心流程

  1. 读取当前值:线程从内存中读取变量的当前值(称为 预期值,Expected Value)。

  2. 计算新值:基于当前值计算出新值。

  3. 比较并交换

    • 如果内存中的当前值等于预期值 → 更新为新值。
    • 如果不等于 → 放弃更新(可能重试)。

示例
假设变量 value 初始为5,线程A和线程B都想将其加1:

  • 线程A读取 value=5,计算新值6 → 执行CAS:内存值仍为5 → 更新为6(成功)。
  • 线程B读取 value=6,计算新值7 → 执行CAS:内存值为6 → 更新为7(成功)。

二、Java中的CAS实现

  1. Atomic类(如 AtomicInteger
    Java提供原子类,底层通过 Unsafe 类调用CPU指令实现CAS。

    AtomicInteger count = new AtomicInteger(5);  
    count.incrementAndGet(); // 内部通过CAS实现自增  
    
  2. Unsafe类
    Java通过 Unsafe 类直接操作内存,调用本地方法(如 compareAndSwapInt)。

    public final native boolean compareAndSwapInt(  
        Object obj, long offset, int expected, int newValue  
    );  
    
  3. 硬件支持

    • x86架构的CPU指令 CMPXCHG 直接支持CAS操作。
    • ARM架构通过 LDREX/STREX 指令实现类似功能。

三、CAS的优缺点

优点缺点
无锁,减少线程阻塞和切换开销ABA问题(后文详解)
高并发场景性能优于锁自旋可能导致CPU资源浪费
简单轻量,适合简单原子操作仅支持单一变量,复杂操作需组合

四、ABA问题及解决方案

  1. ABA问题

    • 线程A读取变量值为 A → 被线程B修改为 B → 又被线程B改回 A
    • 线程A执行CAS时,认为值未变化,继续操作 → 可能导致逻辑错误
  2. 解决方案

    • 版本号机制:每次修改增加版本号,CAS同时检查值和版本号。

    • AtomicStampedReference:Java提供的带版本号的原子引用类。

      java

      复制

      AtomicStampedReference<Integer> ref = new AtomicStampedReference<>(5, 0);  
      ref.compareAndSet(5, 6, 0, 1); // 检查值5和版本号0,更新为值6和版本号1  
      

五、CAS的适用场景

  1. 计数器:如 AtomicInteger 的自增操作。
  2. 无锁数据结构:如无锁队列、栈。
  3. 状态标记:如线程池的状态控制。
  4. 资源分配:如数据库连接池的并发分配。

六、对比锁机制

对比项CAS锁(synchronized/Lock)
线程阻塞无阻塞(自旋重试)可能阻塞(等待锁释放)
性能高并发下性能更优高竞争下性能下降
适用操作简单原子操作复杂同步逻辑
内存开销锁对象和队列占用内存

七、代码示例

public class CASExample {  
    private static AtomicInteger counter = new AtomicInteger(0);  

    public static void main(String[] args) {  
        // 多线程并发自增  
        for (int i = 0; i < 10; i++) {  
            new Thread(() -> {  
                for (int j = 0; j < 1000; j++) {  
                    counter.incrementAndGet(); // CAS实现原子自增  
                }  
            }).start();  
        }  

        // 等待所有线程完成  
        try { Thread.sleep(2000); } catch (InterruptedException e) {}  
        System.out.println("Final count: " + counter.get()); // 输出10000  
    }  
}  

总结

CAS是并发编程的基石

  • 通过硬件支持的原子操作实现无锁并发。
  • 适合简单原子操作,但需注意ABA问题和自旋开销。
  • 在Java中广泛应用于 Atomic 类、ConcurrentHashMap 等并发容器。

口诀
「CAS无锁真高效,比较交换是核心
Atomic类里显身手,自旋重试要记牢
ABA问题版本号,高并发下性能高!」