以ReentrantLock为例解释AQS的工作流程 AQS及其子类的协作思考AQS是怎么利用模板方法完成工作流程的AQS做了哪些如果子类要具体实现子类有需要完成哪些功能或方法AQSAbstractQueuedSynchronizer是整个JUC包同步器的基石。它使用模板方法模式把复杂的线程排队、阻塞、唤醒等通用流程固化在父类中而把“资源是否可用”的判断逻辑交给子类去决定。这是一个非常经典的“你定规则我管排队”的分工协作。 AQS的模板方法定义不可变的算法骨架AQS提供了一系列public final方法作为模板方法例如acquire()、release()这些方法定义了获取或释放资源的整个操作流程并且不允许子类覆盖以保证线程调度逻辑的绝对安全。以独占式获取锁的模板方法acquire(int arg)为例它封装了一个完整的获取锁流程publicfinalvoidacquire(intarg){if(!tryAcquire(arg)// 1. 尝试获取锁具体逻辑由子类实现acquireQueued(addWaiter(Node.EXCLUSIVE),arg)// 2. 获取失败则包装线程并入队等待)selfInterrupt();// 3. 如果在等待过程中被中断则自我中断}在这个流程中tryAcquire(arg)就是那个由子类实现的“钩子方法”。AQS定义好了“骨架”子类只需填入核心逻辑即可。 类比理解银行办事大厅我们可以把AQS的工作机制比作一个银行办事大厅这会非常直观state (同步状态)大厅里的空闲柜台数量是一个volatile int变量。AQS提供了getState(),setState(),compareAndSetState()三个原子方法来操作它。CLH队列 (同步队列)大厅里用于排队的等候区。当柜台被占满没抢到柜台的客户线程会被封装成一个Node节点加入到一个双向队列的尾部等待叫号。ConditionObject (条件队列)大厅里的VIP休息室。当某些业务无法办理时如从ATM取钱但现金不足客户会去条件队列等待直到条件满足再被唤醒重新回到CLH队列排队。 AQS做了什么—— 封装了“等待”与“通知”的复杂细节AQS的核心工作就是封装了上面比喻中管理“排队叫号”这种繁琐但通用的流程它主要做了以下几件事负责管理state安全地维护一个核心的volatile int state并提供原子操作方法。维护同步队列CLH队列当线程获取锁失败时自动将其包装并安全地加入队列同时管理队列的入队和出队。处理线程阻塞与唤醒当线程在队列中排队时精准地将其park阻塞当资源被释放时再精准地unpark唤醒下一个等待的线程。 子类需要做什么—— 聚焦于“资源是否可用”的业务判断AQS规定了5个可以被重写的方法子类通过实现这些方法来定义自己的同步语义。这些方法的核心职责就是安全地判断并修改state的值以判断资源是否可用。以下是需要实现的几个关键方法方法适用模式你的核心任务tryAcquire(int arg)独占模式尝试获取资源。根据state的值判断资源是否空闲。若是则通过CAS原子性地更新state并返回true否则返回false。tryRelease(int arg)独占模式尝试释放资源。将state修改回空闲状态通常需要保证只有持有锁的线程才能释放并返回true。tryAcquireShared(int arg)共享模式尝试获取共享资源。返回值语义更丰富负数表示失败0表示成功但无剩余资源正数表示成功且还有剩余资源。tryReleaseShared(int arg)共享模式尝试释放共享资源。将state增加表示资源变多并返回true。isHeldExclusively()独占模式判断当前线程是否独占资源。主要用于ConditionObject以判断当前线程是否持有锁。 总结AQS通过模板方法模式将通用的线程调度逻辑与具体的资源获取逻辑分离开来。子类不再需要关心复杂的线程管理只需专注于实现上述5个钩子方法通过修改state来定义自己的同步语义。这正是Java并发包灵活性与强大扩展能力的基石。结合ReentrantLock说明核心思考AQS做了什么ReentrantLock做了什么他们是怎么协作工作的下面以ReentrantLock为具体例子重新梳理AQS和ReentrantLock各自做了什么以及它们如何协作完成锁机制。一、AQS 做了什么 —— “排队管理器”AQSAbstractQueuedSynchronizer是一个同步队列阻塞/唤醒的通用框架它封装了一个volatile int state状态变量表示资源数量。在 ReentrantLock 中state表示锁的重入次数0表示无锁0表示被某线程持有且值为重入次数。一个双向 CLH 队列同步队列存放等待获取锁的线程。线程的阻塞LockSupport.park与唤醒unpark。条件队列ConditionObject实现await/signal的基础。模板方法acquire(),release(),acquireShared(),releaseShared()等定义好“获取资源失败则入队阻塞释放资源则唤醒后继”的标准流程。核心思想AQS 只负责“如果资源不够该怎么排队、阻塞、唤醒”而资源是否够的判断逻辑由子类同步器通过钩子方法提供。二、ReentrantLock 做了什么 —— “锁语义实现者”ReentrantLock 是一个可重入的独占锁。它没有从零实现排队阻塞而是内部组合了一个继承 AQS 的同步器Sync并利用 AQS 的能力只实现以下“判断逻辑”定义state的含义0表示未锁定0表示当前线程重复获取的次数。实现tryAcquire(int acquires)尝试直接获取锁。检查state。如果state0尝试 CAS 设置stateacquires并记录当前持有线程为自身 → 成功。如果state0且当前线程就是持有线程 → 增加state值 → 成功重入。否则 → 失败。实现tryRelease(int releases)尝试释放锁。计算新的state state - releases。如果新state 0说明完全释放清除持有线程记录返回true否则返回false仍被当前线程重入持有。实现isHeldExclusively()判断锁是否被当前线程独占。提供公平/非公平策略通过不同的tryAcquire实现。非公平tryAcquire中一上来就直接 CAS 抢锁不管同步队列中是否已有等待线程。公平tryAcquire中先检查同步队列中是否有前驱节点hasQueuedPredecessors()若有则自己排队避免插队。注意ReentrantLock 的lock()和unlock()方法其实只是调用了内部 Sync 的acquire(1)和release(1)而这两个方法是 AQS 定义好的模板方法。三、它们如何协同工作 —— 以lock()为例我们以非公平锁为例画出完整流程1. 调用ReentrantLock.lock()publicvoidlock(){sync.acquire(1);// sync 是继承 AQS 的子类实例}2. 进入 AQS 的acquire(int arg)模板方法publicfinalvoidacquire(intarg){if(!tryAcquire(arg)// ① 尝试获取锁acquireQueued(addWaiter(Node.EXCLUSIVE),arg)){// ② 失败入队等待selfInterrupt();// ③ 自我中断}}3. 调用钩子方法tryAcquire(1)由 ReentrantLock 实现非公平策略下立即读取state如果state 0则尝试 CAS 设置state 1并设置独占线程为当前线程 →成功则直接获得锁tryAcquire返回trueacquire方法结束。如果state 0且当前线程就是持有线程则state重入成功返回true。否则返回false。4. 如果tryAcquire返回false获取锁失败addWaiter将当前线程包装成一个Node节点并加入同步队列的尾部。acquireQueued进入自旋。判断当前节点的前驱是否为 head即自己是否是队列中第一个等待者。如果是再次调用tryAcquire(1)尝试获取锁又给了一次机会。如果获取成功则设置该节点为新的 head并返回。如果获取失败则检查是否应该阻塞shouldParkAfterFailedAcquire然后调用park()阻塞当前线程。5. 当锁被释放时调用ReentrantLock.unlock()publicvoidunlock(){sync.release(1);}进入 AQS 的release(int arg)模板方法publicfinalbooleanrelease(intarg){if(tryRelease(arg)){Nodehhead;if(h!nullh.waitStatus!0)unparkSuccessor(h);// 唤醒 head 的后继节点returntrue;}returnfalse;}tryRelease(1)由 ReentrantLock 实现减少state如果state变为0清除独占线程返回true。当tryRelease返回true后AQS 会唤醒同步队列中的下一个线程使其继续执行acquireQueued中的自旋尝试获取锁。四、公平与非公平的具体差异在tryAcquire中策略tryAcquire实现核心非公平if (state 0 CAS set state to 1) success→直接抢无视队列公平if (state 0 !hasQueuedPredecessors() CAS success)→队列有等待者则自己排队其余所有排队、阻塞、唤醒逻辑全部复用 AQS 代码一个字都不用改。五、总结模块负责的内容关键代码/方法AQS同步队列维护、线程 park/unpark、状态原子更新、模板流程acquire,releaseacquire(),release(),addWaiter(),acquireQueued(),unparkSuccessor()ReentrantLock锁的语义可重入、公平/非公平、实现tryAcquire/tryRelease来操作state、记录独占线程NonfairSync.tryAcquire(),FairSync.tryAcquire(),Sync.tryRelease()一句话AQS 负责“如何排队”ReentrantLock 负责“什么情况下可以插队/重入”。两者通过模板方法模式完美解耦这也是 JUC 中Semaphore,CountDownLatch,ReentrantReadWriteLock等工具都基于 AQS 的原因。