跳到正文
Elaine Blog
返回

AQS 与 ReentrantLock 源码主线

并发编程

阅读 AQS 不需要逐行记忆实现。抓住“尝试修改同步状态,失败后排队并阻塞,释放时唤醒后继”这条主线,就能解释 ReentrantLockSemaphoreCountDownLatch 的共同骨架与不同语义。

Table of contents

Open Table of contents

先正确使用 ReentrantLock

private final java.util.concurrent.locks.ReentrantLock lock =
    new java.util.concurrent.locks.ReentrantLock();

void update() {
    lock.lock();
    try {
        // 在同一临界区维护业务不变量
    } finally {
        lock.unlock();
    }
}

若忘记 finally,异常会让锁永远不释放。需要取消等待时使用 lockInterruptibly();需要截止时间时使用带超时的 tryLock

AQS 的五段主线

**AQS(AbstractQueuedSynchronizer,抽象队列同步器)**使用一个原子 int state 和 FIFO 等待队列构建同步器。

  1. tryAcquiretryAcquireShared 由子类解释 state 并尝试获取。
  2. 快速路径失败后,AQS 把当前线程对应的节点加入等待队列。
  3. 线程在不满足获取条件时通过 LockSupport.park 暂停。
  4. 释放方通过 tryReleasetryReleaseShared 修改状态。
  5. AQS 选择合适后继并 unpark,被唤醒线程重新竞争。

被唤醒不等于已经拿到锁。线程必须重新检查状态,这与条件等待中的循环检查是同一类原则。

ReentrantLock 如何解释 state

独占锁中,state=0 表示未持有;首次获取把它设为 1 并记录拥有线程。同一线程再次获取时增加计数,释放时递减到 0 才 真正开放给其他线程。这就是可重入。

非公平锁允许新到线程在队列线程之前尝试 CAS,通常吞吐更好。公平锁会检查是否存在排队前驱,降低插队,但不能保证 线程调度的绝对公平,并可能降低吞吐。

Condition 使用独立等待队列

Condition.await() 会释放锁并把线程放入条件队列;signal() 把等待节点转移到同步队列,线程仍需重新获取锁后才能 返回。调用这些方法前必须持有对应锁。

多个 Condition 可以把“队列非空”和“队列未满”分成不同等待集,比一个对象上的 notifyAll() 更精确。业务代码仍应 优先使用 BlockingQueue 等现成组件。

Java 25 的 AQS API 明确区分独占与共享获取,并说明同步器子类只实现状态语义,排队机制由 AQS 负责。

下一步

阅读Semaphore、CountDownLatch 与 CyclicBarrier,按资源许可、一次汇合和 重复阶段选择协调器。


分享这篇文章:

上一篇
CAS、ABA 与 Atomic 原子类
下一篇
Semaphore、CountDownLatch 与 CyclicBarrier