阅读 AQS 不需要逐行记忆实现。抓住“尝试修改同步状态,失败后排队并阻塞,释放时唤醒后继”这条主线,就能解释
ReentrantLock、Semaphore 和 CountDownLatch 的共同骨架与不同语义。
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 等待队列构建同步器。
tryAcquire或tryAcquireShared由子类解释state并尝试获取。- 快速路径失败后,AQS 把当前线程对应的节点加入等待队列。
- 线程在不满足获取条件时通过
LockSupport.park暂停。 - 释放方通过
tryRelease或tryReleaseShared修改状态。 - AQS 选择合适后继并
unpark,被唤醒线程重新竞争。
被唤醒不等于已经拿到锁。线程必须重新检查状态,这与条件等待中的循环检查是同一类原则。
ReentrantLock 如何解释 state
独占锁中,state=0 表示未持有;首次获取把它设为 1 并记录拥有线程。同一线程再次获取时增加计数,释放时递减到 0 才
真正开放给其他线程。这就是可重入。
非公平锁允许新到线程在队列线程之前尝试 CAS,通常吞吐更好。公平锁会检查是否存在排队前驱,降低插队,但不能保证 线程调度的绝对公平,并可能降低吞吐。
Condition 使用独立等待队列
Condition.await() 会释放锁并把线程放入条件队列;signal() 把等待节点转移到同步队列,线程仍需重新获取锁后才能
返回。调用这些方法前必须持有对应锁。
多个 Condition 可以把“队列非空”和“队列未满”分成不同等待集,比一个对象上的 notifyAll() 更精确。业务代码仍应
优先使用 BlockingQueue 等现成组件。
Java 25 的 AQS API 明确区分独占与共享获取,并说明同步器子类只实现状态语义,排队机制由 AQS 负责。
下一步
阅读Semaphore、CountDownLatch 与 CyclicBarrier,按资源许可、一次汇合和 重复阶段选择协调器。