AQS通过state变量、FIFO队列和模板方法提供同步基础设施;独占模式用tryAcquire/tryRelease,共享模式用tryAcquireShared/tryReleaseShared,二者共用队列但唤醒逻辑不同。

Java 中基于 AQS(AbstractQueuedSynchronizer)实现自定义同步组件,核心在于正确重写其模板方法,并区分独占模式与共享模式的语义和行为。AQS 本身不直接提供锁逻辑,而是通过 state 状态变量 + FIFO 同步队列 + 模板方法协作机制,为上层提供统一的线程阻塞/唤醒、排队、状态管理基础设施。
理解 AQS 的两种模式本质
独占模式(Exclusive)强调“排他性”:同一时刻最多一个线程能获取成功(如 ReentrantLock),其他线程必须等待;共享模式(Shared)允许多个线程同时获取成功(如 Semaphore、CountDownLatch),只要资源允许。
- 独占模式由
tryAcquire/tryRelease控制,AQS 内部调用它们决定是否入队或唤醒后继 - 共享模式由
tryAcquireShared/tryReleaseShared控制,返回值为负数表示失败、0 表示获取但无剩余、正数表示成功且还有剩余资源可被其他线程获取 - 两种模式的等待队列是同一套,但唤醒逻辑不同:独占模式只唤醒一个后继,共享模式可能连续唤醒多个(需在
doAcquireShared中循环检查)
编写一个可重入的独占锁(ReentrantLock 简化版)
继承 AbstractQueuedSynchronizer,维护 state 表示持有次数,记录当前持有线程以支持可重入:
static class SimpleReentrantLock extends AbstractQueuedSynchronizer {
// 尝试获取独占锁
protected boolean tryAcquire(int acquires) {
Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
} else if (getExclusiveOwnerThread() == current) {
setState(c + acquires); // 可重入:同一线程多次 acquire
return true;
}
return false;
}
<pre class="brush:php;toolbar:false;">// 尝试释放独占锁
protected boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = (c == 0);
if (free)
setExclusiveOwnerThread(null);
setState(c);
return free;
}
// 提供对外 API
void lock() { acquire(1); }
void unlock() { release(1); }}
注意:acquire(1) 和 release(1) 是 AQS 提供的模板方法,内部已封装排队、阻塞、唤醒等逻辑,无需手动处理线程调度。
编写一个带计数的共享锁(类似 Semaphore)
实现一个简单信号量:初始化时指定许可数,acquire() 减少许可,release() 增加许可,允许多个线程并发获取:
static class SimpleSemaphore extends AbstractQueuedSynchronizer {
SimpleSemaphore(int permits) {
setState(permits);
}
<pre class="brush:php;toolbar:false;">// 共享模式获取:尝试减少一个许可
protected int tryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining <p>}</p>关键点:tryAcquireShared 返回值决定了后续行为——返回 ≥ 0 时,AQS 会继续唤醒后续共享节点;返回负数则当前线程入队等待。
注意事项与常见陷阱
实际开发中容易忽略的细节:
-
tryAcquire/tryRelease必须是原子、无锁、快速失败的;避免在其中做 I/O、锁竞争或长时间计算 - 所有对
state的修改必须使用compareAndSetState或setState(后者仅限已知当前线程持有锁时安全使用) - 不要重写 AQS 的同步方法(如
acquire),只重写模板方法;否则会绕过队列管理逻辑 - 调试时可通过
getQueueLength()、hasQueuedThreads()辅助验证排队行为 - 若需支持条件队列(如
Condition),还需实现newCondition()并配合await/signal逻辑
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











