2026年7月11日 · 5 分钟阅读

JUC 核心原理:CAS、Atomic、Lock、AQS 与 ReentrantLock

梳理 JUC 中 Atomic 原子类、CAS 原理与缺陷、Java 锁分类、synchronized 和 JUC 锁对比、AQS 队列以及 ReentrantLock 获取和释放锁流程。

JUC 是 java.util.concurrent 包的简称,是 Java 并发编程的核心工具箱。

如果说 synchronizedvolatile 是并发基础关键字,那么 JUC 提供的是更丰富的工程化能力:原子类、显式锁、同步器、并发容器、阻塞队列、线程池。

Atomic 原子类

Atomic 包提供了一组基于 CAS 的原子操作类。

常见类型包括:

类型示例
基本类型原子类AtomicIntegerAtomicLongAtomicBoolean
引用类型原子类AtomicReference
数组原子类AtomicIntegerArray
字段更新器AtomicIntegerFieldUpdater
高并发累加器LongAdderLongAccumulator

例如计数器可以这样写:

AtomicInteger count = new AtomicInteger();
count.incrementAndGet();

它比 volatile int count; count++; 更安全,因为递增操作是原子的。

CAS 是什么

CAS 全称 Compare And Swap,比较并交换。

它包含三个值:

  • 内存位置 V。
  • 预期旧值 A。
  • 准备写入的新值 B。

只有当 V 当前值等于 A 时,才把 V 更新为 B;否则说明其他线程改过了,本次更新失败。

CAS 是乐观锁思想:先不加锁,直接尝试更新;失败了再重试。

CAS 的优势和问题

CAS 的优势是避免阻塞线程,竞争不激烈时性能很好。

但它也有缺陷:

问题说明
ABA 问题值从 A 变成 B 又变回 A,CAS 看不出中间变化
自旋开销竞争激烈时反复重试会消耗 CPU
单变量限制普通 CAS 只能保证一个变量的原子更新

ABA 问题可以用版本号解决,例如 AtomicStampedReference

Java 锁分类

Java 锁可以从多个维度分类。

分类维度类型
是否阻塞悲观锁、乐观锁
是否可重入可重入锁、不可重入锁
是否公平公平锁、非公平锁
是否共享独占锁、共享锁
是否可中断可中断锁、不可中断锁

synchronizedReentrantLock 都是可重入的独占锁。

synchronized 和 ReentrantLock 对比

能力synchronizedReentrantLock
实现方式JVM 关键字JUC 类
自动释放支持,异常时自动释放需要 finally 手动释放
可中断获取锁不支持支持
超时获取锁不支持支持
公平锁不支持直接配置支持公平/非公平
多条件队列依赖 wait/notify支持多个 Condition

如果只是简单临界区,synchronized 更简洁;如果需要超时、中断、公平锁、多个等待条件,ReentrantLock 更灵活。

AQS 是什么

AQS 全称 AbstractQueuedSynchronizer,队列同步器。它是很多 JUC 同步组件的基础。

基于 AQS 实现的组件包括:

  • ReentrantLock
  • ReentrantReadWriteLock
  • Semaphore
  • CountDownLatch
  • FutureTask

AQS 的核心思想是:用一个 state 表示同步状态,用一个 FIFO 双向队列管理获取同步状态失败的线程。

AQS 同步队列

当线程获取锁失败时,会被包装成 Node 节点,加入 AQS 同步队列。

可以简化理解为:

head <-> Node(threadA) <-> Node(threadB) <-> tail

队列中的线程会按一定规则挂起和唤醒。锁释放后,AQS 会唤醒后继节点,让它重新竞争同步状态。

ReentrantLock 获取锁流程

ReentrantLock.lock() 最终会委托内部 Sync 对象获取锁。

简化流程:

  1. 尝试用 CAS 把 state 从 0 改为 1。
  2. 成功则当前线程成为锁持有者。
  3. 如果锁已经被当前线程持有,则 state + 1,实现重入。
  4. 如果获取失败,线程进入 AQS 队列。
  5. 队列中的线程自旋判断自己是否有资格获取锁。
  6. 不满足条件时通过 LockSupport.park() 挂起。

可重入的本质就是同一个线程再次获取锁时不阻塞,而是增加重入计数。

ReentrantLock 释放锁流程

释放锁时调用 unlock(),最终会减少 state

简化流程:

  1. 判断当前线程是否为锁持有者。
  2. 如果不是,抛出异常。
  3. 如果是,将 state 减 1。
  4. 如果 state 还不为 0,说明只是释放了一层重入。
  5. 如果 state 变为 0,锁完全释放。
  6. 唤醒 AQS 队列中的后继线程。

使用 ReentrantLock 必须在 finally 中释放锁:

lock.lock();
try {
    // critical section
} finally {
    lock.unlock();
}

公平锁和非公平锁

公平锁会按照队列顺序获取锁,先来先得。非公平锁允许线程插队,直接尝试抢锁。

类型优点缺点
公平锁等待时间更可控吞吐量可能较低
非公平锁吞吐量通常更高可能出现线程等待更久

ReentrantLock 默认是非公平锁,因为它在多数场景下性能更好。

读写锁

ReentrantReadWriteLock 把锁分成读锁和写锁。

  • 多个读线程可以同时持有读锁。
  • 写线程持有写锁时,其他读写线程都要等待。

适合读多写少的场景,比如配置缓存、路由表、规则表等。

小结

JUC 的核心可以这样串起来:

  1. Atomic 原子类依赖 CAS 实现无锁更新。
  2. CAS 适合低竞争场景,但有 ABA 和自旋开销问题。
  3. Lock 提供比 synchronized 更灵活的锁能力。
  4. AQS 用 state 和 FIFO 队列抽象同步器。
  5. ReentrantLock 是理解 AQS 的最佳入口。

掌握 AQS 后,再看 CountDownLatchSemaphoreFutureTask 等工具,就会发现它们只是同步状态和队列规则不同。