ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

AQS 原理详解

2026/8/15 11:25:59 拓冰建站 浏览量
AQS 原理详解 一、什么是 AQS它的作用是什么AQS 全称是 AbstractQueuedSynchronizer即抽象队列同步器它是 Java 并发包的核心基础组件。AQS是一种提供了原子式管理同步状态、阻塞和唤醒线程等功能并提供等待队列模型的线程同步框架。AQS 的核心作用可以概括为两点统一管理同步状态通过 state 变量统一表示锁的占用情况比如 ReentrantLock 用 state 表示重入次数Semaphore 用 state 表示剩余许可数量。统一管理线程排队当线程获取锁失败时不需要自己实现等待队列AQS 会自动将线程包装成 Node 节点放入 CLH 队列中并负责线程的阻塞、唤醒和排队逻辑。二、AQS 的组成包含哪些内容AQS 的内部结构主要由以下几个核心部分组成同步状态 state一个volatile修饰的int类型全局成员变量state来表示同步状态在排队加锁的过程中通过CAS完成对state值的修改。CLH 等待队列一个虚拟的 FIFO 双向队列队列中的每个节点代表一个等待获取锁的线程。队列的头节点是当前持有锁的线程后续节点按顺序排队等待。Node 节点等待队列中的每个线程都被封装成一个 Node 对象Node 中记录了线程引用、等待状态waitStatus、前驱节点和后继节点的引用。三、AQS 独占锁的获取流程独占锁的获取主要入口是 acquire() 方法整个流程可以分为以下几个步骤步骤一快速尝试获取锁首先调用 tryAcquire(arg) 方法由子类实现具体的获取逻辑。如果 tryAcquire 返回 true说明获取锁成功方法直接返回线程继续执行后续业务代码。步骤二入队操作如果 tryAcquire 返回 false说明锁已被其他线程占用当前线程需要排队。AQS 会调用 addWaiter(Node.EXCLUSIVE) 方法将当前线程包装成一个独占模式的 Node 节点并通过 CAS 操作将其添加到 CLH 队列的尾部。步骤三自旋阻塞入队后调用 acquireQueued方法进入自旋等待状态。线程会不断检查自己的前驱节点是否为头节点如果是头节点就再次尝试获取锁tryAcquire。如果获取成功将自己设置为新的头节点并返回中断状态。步骤四挂起线程如果前驱节点不是头节点或者再次尝试获取锁失败AQS 会检查前驱节点的 waitStatus。如果前驱节点的 waitStatus 为 SIGNAL说明前驱节点承诺会在释放锁时唤醒自己当前线程就可以安全地调用 LockSupport.park(this) 将自己挂起等待被唤醒。步骤五锁释放与唤醒当持有锁的线程调用 release(arg) 方法时AQS 会调用 tryRelease(arg) 释放锁。如果释放成功会检查头节点的 waitStatus如果为 SIGNAL就通过 unparkSuccessor 方法唤醒头节点的后继节点被唤醒的线程会从 park 处继续执行重新进入自旋获取锁的流程。整个流程可以概括为尝试获取 → 获取失败则入队 → 判断前驱节点 → 根据情况自旋或挂起 → 等待前驱节点释放后唤醒。四、共享模式和独占模式有什么区别独占模式下同一时刻只有一个线程可以持有锁其他线程必须排队等待典型代表是 ReentrantLock。共享模式下同一时刻允许多个线程同时持有锁。独占模式适用于互斥资源访问适用于写多读少的场景共享模式适用于读多写少的场景比如读锁允许多个线程同时读取数据提高并发性能。五、AQS 的公平锁和非公平锁有什么区别公平锁和非公平锁的区别主要体现在新线程尝试获取锁时的行为以 ReentrantLock 为例公平锁FairSync在调用 tryAcquire 时即使当前 state 为 0锁未被占用公平锁也不会直接获取锁而是先检查 CLH 队列中是否有其他线程等待了更长时间。如果队列中已有等待线程当前线程会放弃抢占直接加入队尾排队。这保证了先到先得的原则线程按请求顺序获取锁。非公平锁NonfairSync在调用 tryAcquire 时如果 state 为 0非公平锁会直接尝试通过 CAS 修改 state 来抢占锁不关心队列中是否有等待线程。新来的线程有可能插队直接抢到锁而已经在队列中等待的线程可能继续等待。非公平锁在多线程竞争激烈时性能通常更高因为它减少了线程切换和唤醒的开销。性能差异非公平锁由于允许插队减少了线程上下文切换的次数整体吞吐量更高是 ReentrantLock 的默认实现。公平锁虽然保证了公平性但每次获取锁都需要检查队列且频繁的线程切换会导致性能下降。