Java 并发:CAS + AQS 超通俗详解(原理、场景、代码、技术栈、对比)

一、先搞懂两个核心基础概念(通俗版)

1. CAS:无锁乐观锁(Compare And Swap,比较并交换)

通俗理解

不使用操作系统内核锁,纯 CPU 硬件指令实现并发修改,全程用户态,无上下文切换,性能极高。
 
操作三步原子完成(CPU 保证不可拆分):
  1. 拿到内存旧值 V
  2. 预期值 E(线程本地缓存的值)
  3. 新值 N
  • 如果 V == E:没人修改过,直接更新为 N,返回成功
  • 如果 V != E:中间被其他线程改了,更新失败,自旋重试
优点:无内核锁阻塞、吞吐量高
 
缺点:自旋空耗 CPU、ABA 问题、只能保证单个变量原子操作

底层依赖

CPU 指令:cmpxchg(x86 架构),Java 通过Unsafe类调用本地方法封装 CAS 操作。

2. AQS:抽象队列同步器(AbstractQueuedSynchronizer)

通俗理解

锁的底层框架模板,JDK 所有锁(ReentrantLock/CountDownLatch/Semaphore 等)底层全部基于 AQS 实现。
 
核心三要素:
  1. state 同步状态(int 变量):核心标记,CAS 修改 state 实现加锁 / 释放
    • 独占锁:state=0 无锁,state=1 持有锁,>1 代表可重入次数
    • 共享锁:state 代表许可数 / 剩余等待线程数
  2. 双向 FIFO 等待队列(CLH 队列):抢锁失败的线程封装成 Node 节点排队,阻塞挂起
  3. 独占 / 共享两套模式:独占(排他锁,ReentrantLock)、共享(读写锁、CountDownLatch)
本质:AQS 底层大量依赖 CAS修改 state、队列头节点,CAS 是 AQS 的底层基石!
 
关系总结:AQS = 并发锁框架,CAS = AQS 实现同步的底层原子工具

二、CAS 完整详解

1. CAS 应用场景

场景 1:原子计数器(AtomicInteger/AtomicLong)

适合:简单数字自增、统计、并发计数,不需要复杂锁逻辑

场景 2:无锁并发容器(ConcurrentHashMap 元素更新)

JDK8 ConcurrentHashMap 节点值更新、size 统计底层全是 CAS

场景 3:自旋锁手动实现(自定义乐观锁)

场景 4:AQS 底层修改 state、队列头节点(AQS 离不开 CAS)

场景 5:原子引用、解决 ABA(AtomicStampedReference)

2. CAS 核心技术栈

  1. 底层硬件:CPU cmpxchg 原子指令
  2. Java 底层类:sun.misc.Unsafe(提供 native CAS 方法)
  3. JDK 工具包:java.util.concurrent.atomic 全系列原子类
  4. 上层框架:ConcurrentHashMap、AQS、Redis 分布式锁(CAS 思想)

3. CAS 代码示例

示例 1:原生 Unsafe 手写 CAS 计数器(最底层)

java
 
运行
 
 
 
import sun.misc.Unsafe;
import java.lang.reflect.Field;

public class CasUnsafeDemo {
    // 共享变量
    private volatile int count = 0;
    // Unsafe对象
    private static final Unsafe UNSAFE;
    // count变量内存偏移地址
    private static final long COUNT_OFFSET;

    static {
        try {
            // 反射获取Unsafe实例
            Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
            unsafeField.setAccessible(true);
            UNSAFE = (Unsafe) unsafeField.get(null);
            // 获取count字段内存偏移
            COUNT_OFFSET = UNSAFE.objectFieldOffset(CasUnsafeDemo.class.getDeclaredField("count"));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    // 基于CAS实现自增
    public void increment() {
        // 自旋:CAS失败就循环重试
        while (true) {
            int oldVal = UNSAFE.getIntVolatile(this, COUNT_OFFSET);
            int newVal = oldVal + 1;
            // CAS:如果当前内存值=oldVal,更新为newVal,返回true成功
            boolean success = UNSAFE.compareAndSwapInt(this, COUNT_OFFSET, oldVal, newVal);
            if (success) {
                break;
            }
            // CAS失败,循环重试(自旋)
        }
    }

    public int getCount() {
        return UNSAFE.getIntVolatile(this, COUNT_OFFSET);
    }

    public static void main(String[] args) throws InterruptedException {
        CasUnsafeDemo demo = new CasUnsafeDemo();
        // 10个线程,每个自增10000次
        Thread[] threads = new Thread[10];
        for (int i = 0; i < 10; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < 10000; j++) {
                    demo.increment();
                }
            });
            threads[i].start();
        }
        // 等待所有线程完成
        for (Thread t : threads) t.join();
        // 输出预期100000,无并发丢失
        System.out.println("最终计数:" + demo.getCount());
    }
}
 

示例 2:JDK 封装 AtomicInteger(业务常用,屏蔽 Unsafe 底层)

java
 
运行
 
 
 
import java.util.concurrent.atomic.AtomicInteger;

public class AtomicIntegerCasDemo {
    // 原子整数,底层封装CAS
    private static final AtomicInteger count = new AtomicInteger(0);

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 5000; i++) {
                count.getAndIncrement(); // 内部就是CAS自旋
            }
        });
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 5000; i++) {
                count.getAndIncrement();
            }
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        // 输出10000,线程安全
        System.out.println("count = " + count.get());
    }
}
 

示例 3:CAS 经典问题:ABA + 解决方案 AtomicStampedReference

ABA 问题说明

线程 A 读取值 A,线程 B 先改成 B 再改回 A;线程 A 执行 CAS 时判断相等,误以为没修改,造成数据错乱。
java
 
运行
 
 
 
import java.util.concurrent.atomic.AtomicStampedReference;

public class CasABADemo {
    // 带版本戳的原子引用,解决ABA
    static AtomicStampedReference<Integer> ref = new AtomicStampedReference(100, 1);

    public static void main(String[] args) throws InterruptedException {
        // 线程1:模拟读取后阻塞
        Thread t1 = new Thread(() -> {
            int stamp = ref.getStamp();
            Integer oldVal = ref.getReference();
            try {
                Thread.sleep(1000); // 睡眠,让t2修改
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // CAS:同时校验值+版本号
            boolean success = ref.compareAndSet(oldVal, 200, stamp, stamp + 1);
            System.out.println("线程1CAS结果:" + success);
        });

        // 线程2:完成A->B->A
        Thread t2 = new Thread(() -> {
            int stamp = ref.getStamp();
            ref.compareAndSet(100, 101, stamp, stamp + 1);
            ref.compareAndSet(101, 100, ref.getStamp(), ref.getStamp() + 1);
            System.out.println("线程2完成ABA修改");
        });

        t1.start();
        t2.start();
        t1.join();
        t2.join();
    }
}
 
输出:线程 1CAS 结果:false,成功拦截 ABA 问题。

4. CAS 优缺点总结

优点

  1. 用户态无锁,不切换内核,性能远超 synchronized 重量级锁
  2. 无线程阻塞,不会出现线程挂起唤醒开销

缺点

  1. 自旋循环空耗 CPU,高并发竞争激烈时 CPU 占用飙升
  2. ABA 数据一致性问题
  3. 仅支持单个变量原子操作,无法同时操作多个共享变量

三、AQS 完整详解

1. AQS 应用场景(JDK 所有同步工具底层)

独占锁模式(state 标记持有锁)

  1. ReentrantLock 可重入排他锁(最常用)
  2. ReentrantReadWriteLock.WriteLock 写锁(独占)

共享锁模式(state 代表许可 / 资源)

  1. ReentrantReadWriteLock.ReadLock 读锁(共享,多线程同时读)
  2. CountDownLatch 线程等待计数器(等待所有任务完成)
  3. Semaphore 信号量(限流、控制并发线程数)
  4. CyclicBarrier 循环栅栏(多线程到达屏障同时执行)

2. AQS 技术栈分层

底层依赖

  1. CAS:修改 state 同步状态、CLH 队列头 / 尾节点
  2. volatile:state、Node 节点变量保证可见性
  3. LockSupport.park/unpark:阻塞 / 唤醒等待队列线程(底层调用 Unsafe)

中层:AQS 框架 java.util.concurrent.locks.AbstractQueuedSynchronizer

上层实现类(业务直接使用)

ReentrantLock、ReadWriteLock、CountDownLatch、Semaphore

上层业务框架

线程池 ThreadPoolExecutor、分布式本地限流、自定义锁工具

3. AQS 核心执行流程(通俗版以 ReentrantLock 独占锁举例)

  1. 线程调用 lock (),调用 AQStryAcquire(),CAS 尝试把 state 从 0 改为 1
    • 修改成功:当前线程持有锁,记录持有线程
    • 修改失败:抢锁失败,封装 Node 加入 CLH 双向等待队列
  2. 队列中线程调用LockSupport.park()阻塞休眠,不再占用 CPU
  3. 持有锁线程 unlock () 释放锁:CAS 把 state 减为 0
  4. 唤醒队列头部第一个等待线程,再次竞争锁

4. AQS 代码示例

示例 1:AQS 独占模式:ReentrantLock 基础使用(业务最常用)

java
 
运行
 
 
 
import java.util.concurrent.locks.ReentrantLock;

public class AqsReentrantLockDemo {
    // 基于AQS实现的可重入独占锁
    private static final ReentrantLock lock = new ReentrantLock();
    private static int num = 0;

    public static void add() {
        lock.lock(); // 底层调用AQS acquire()
        try {
            num++;
        } finally {
            lock.unlock(); // 底层调用AQS release(),必须finally释放
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 10000; i++) add();
        });
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 10000; i++) add();
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("num = " + num); // 20000,线程安全
    }
}
 

示例 2:AQS 共享模式:CountDownLatch 等待多线程完成

CountDownLatch 底层 AQS 共享模式,state 初始为计数器,countDown () CAS 减少 state,state=0 唤醒所有等待线程
java
 
运行
 
 
 
import java.util.concurrent.CountDownLatch;

public class AqsCountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        // AQS state初始=5
        CountDownLatch latch = new CountDownLatch(5);
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "任务执行完成");
                latch.countDown(); // CAS将state-1
            }).start();
        }
        latch.await(); // 阻塞,直到state=0
        System.out.println("所有子线程执行完毕,主线程继续");
    }
}
 

示例 3:手写简易独占锁(自定义 AQS 实现,看懂 AQS 模板方法)

AQS 采用模板方法模式,只需要重写tryAcquire/tryRelease,排队、阻塞、唤醒逻辑 AQS 内部封装好
java
 
运行
 
 
 
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.Condition;

// 自定义独占锁,基于AQS实现
class MyAqsLock implements Lock {
    // 内部同步器,继承AQS
    private static class Sync extends AbstractQueuedSynchronizer {
        // 尝试获取独占锁:state=0无锁,CAS改为1成功获取
        @Override
        protected boolean tryAcquire(int arg) {
            // CAS修改state,底层依赖Unsafe
            return compareAndSetState(0, 1);
        }

        // 释放锁:state置0
        @Override
        protected boolean tryRelease(int arg) {
            setState(0);
            return true;
        }

        // 是否持有独占锁
        @Override
        protected boolean isHeldExclusively() {
            return getState() == 1;
        }
    }

    private final Sync sync = new Sync();

    @Override
    public void lock() {
        sync.acquire(1); // AQS内置:抢锁失败入队阻塞
    }

    @Override
    public void unlock() {
        sync.release(1); // AQS内置:释放锁,唤醒队列线程
    }

    // 其余接口简单实现省略
    @Override public boolean tryLock() { return sync.tryAcquire(1); }
    @Override public Condition newCondition() { return sync.newCondition(); }
    @Override public void lockInterruptibly() throws InterruptedException { sync.acquireInterruptibly(1); }
    @Override public boolean tryLock(long time, java.util.concurrent.TimeUnit unit) throws InterruptedException { return sync.tryAcquireNanos(1, unit.toNanos(time)); }
}

// 测试自定义AQS锁
public class CustomAqsDemo {
    private static final MyAqsLock lock = new MyAqsLock();
    private static int count = 0;

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                lock.lock();
                try { count++; } finally { lock.unlock(); }
            }
        });
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                lock.lock();
                try { count++; } finally { lock.unlock(); }
            }
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(count); // 2000
    }
}
 

5. AQS 优缺点

优点

  1. 统一同步框架,一套底层实现所有锁 / 同步工具,复用性极强
  2. 竞争激烈时线程 park 阻塞,不会像 CAS 无限自旋耗 CPU
  3. 支持独占、共享、可重入、公平 / 非公平锁多种能力

缺点

  1. 底层封装复杂,理解门槛高
  2. 线程阻塞唤醒存在少量内核态切换开销,性能略低于纯 CAS 无锁

四、CAS 和 AQS 核心关联(重中之重)

  1. CAS 是 AQS 的底层原子操作基础
     
    AQS 中所有修改 state、队列头尾节点、节点状态的操作,全部使用compareAndSetState(CAS),不加 synchronized 保证原子性。
     
    源码片段(AQS 内部):
    java
     
    运行
     
     
     
    // CAS修改同步状态
    protected final boolean compareAndSetState(int expect, int update) {
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }
    
     
  2. 分层使用场景区分
    • 轻量无竞争:只用 CAS(AtomicInteger、简单计数),无队列、无阻塞,性能拉满
    • 高并发大量竞争:AQS(锁、CountDownLatch),抢不到锁线程入队阻塞,避免 CAS 空自旋
  3. 性能取舍
    • 低并发:CAS > AQS(无阻塞)
    • 高并发大量竞争:AQS > CAS(CAS 自旋 CPU 打满,AQS 线程休眠)

五、CAS vs AQS 场景选型对照表

表格
 
维度CAS(原子类 / 无锁)AQS(锁 / 同步器)
底层 CPU 原子指令,无阻塞自旋 底层 CAS+CLH 队列 + LockSupport 阻塞
适用场景 单个变量并发修改、低竞争计数 多代码块同步、资源限流、线程等待
并发冲突 冲突时循环自旋,消耗 CPU 冲突线程入队挂起,释放 CPU
功能 仅支持单变量原子操作 支持独占 / 共享、可重入、公平锁、条件等待
代表工具 AtomicInteger、ConcurrentHashMap 节点 ReentrantLock、CountDownLatch、Semaphore
典型业务 接口请求计数、序列号生成 接口并发控制、多线程任务汇总、读写分离

六、完整技术栈全景

CAS 全链路技术栈

硬件 CPU cmpxchg → Unsafe native CAS → atomic 原子包 → ConcurrentHashMap → AQS 底层依赖 → Redis 乐观锁、数据库 version 乐观锁

AQS 全链路技术栈

CAS + volatile + LockSupport → AbstractQueuedSynchronizer → ReentrantLock/ReadWriteLock/CountDownLatch/Semaphore → ThreadPoolExecutor 线程池、分布式本地限流、自定义同步工具

七、业务开发选型建议

  1. 简单数字统计、单点变量并发修改 → 优先AtomicXXX(CAS)
  2. 一段代码 / 方法需要整体同步、需要可重入、公平锁 → ReentrantLock(AQS 独占)
  3. 多线程读少写多、读写分离 → ReentrantReadWriteLock(AQS 读写共享)
  4. 控制同时执行线程数量(限流)→ Semaphore(AQS 共享)
  5. 主线程等待一批子线程全部完成 → CountDownLatch(AQS 共享)
  6. 超高并发、竞争极端激烈,用 CAS 会 CPU100% → 改用 AQS 锁
posted @ 2026-06-19 15:35  元一软件  阅读(28)  评论(0)    收藏  举报