使用aqs实现自定义同步器需继承aqs并重写tryacquire/tryrelease等模板方法,依托state变量、clh队列和独占/共享模式三要素构建线程安全组件。

要使用 AbstractQueuedSynchronizer(AQS)实现自定义同步器,核心是继承 AQS 并重写其关键的模板方法,再基于它封装出线程安全的同步组件(如锁、信号量等)。AQS 本身不直接提供同步语义,而是提供统一的等待队列、状态管理与线程阻塞/唤醒机制。
理解 AQS 的三个核心要素
AQS 围绕以下三部分构建:
-
state 状态变量:用 volatile int 表示,代表同步状态(如 ReentrantLock 中的持有次数、Semaphore 中的剩余许可数)。通过
getState()、setState(int)、compareAndSetState(int, int)安全操作。 - CLH 同步队列:一个 FIFO 的双向链表,存储等待获取同步状态的线程节点(Node)。AQS 自动维护该队列,你无需手动操作队列结构。
-
独占/共享模式的获取与释放逻辑:由子类决定如何解释 state,并实现对应的
tryAcquire、tryRelease(独占)或tryAcquireShared、tryReleaseShared(共享)方法。
实现一个简单的不可重入独占锁(MyLock)
这是最典型的入门示例,帮助掌握基本骨架:
public class MyLock implements Lock {
private static class Sync extends AbstractQueuedSynchronizer {
// 尝试获取独占锁:state == 0 表示空闲,CAS 设为 1
protected boolean tryAcquire(int acquires) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
// 尝试释放独占锁:仅当当前线程持有时才允许释放
protected boolean tryRelease(int releases) {
if (getState() == 0) throw new IllegalMonitorStateException();
setExclusiveOwnerThread(null);
setState(0);
return true;
}
}
private final Sync sync = new Sync();
public void lock() {
sync.acquire(1); // 调用 AQS 的 acquire,内部会调用 tryAcquire
}
public void unlock() {
sync.release(1); // 调用 AQS 的 release,内部会调用 tryRelease
}
// 其他接口方法(如 newCondition、lockInterruptibly 等)可按需补充
}
注意:acquire(1) 和 release(1) 中的参数只是约定俗成的“数量”,对本例无实际意义(因不可重入),但必须与 tryAcquire/tryRelease 的参数匹配。
处理可重入、条件等待等进阶能力
若需支持可重入,state 应表示重入次数;若需 Condition,需在 Sync 内部使用 newCondition() 创建条件队列(AQS 提供默认实现):
-
可重入逻辑:在
tryAcquire中先检查当前线程是否已持有锁(getExclusiveOwnerThread() == current),是则 state += 1;否则尝试 CAS 获取。 -
条件等待:Sync 类需继承 AQS 并实现
isHeldExclusively()(判断当前是否独占持有),然后调用sync.newCondition()返回ConditionObject实例。await/signal 由 AQS 自动调度到条件队列和同步队列之间转移节点。 -
中断敏感获取:用
acquireInterruptibly(1)替代acquire(1),使 lock() 可响应中断。
避免常见错误
初学者容易踩的坑包括:
-
忘记设置/清除 owner 线程:独占模式下,
setExclusiveOwnerThread和置 null 必须严格配对,否则isHeldExclusively或可重入判断失效。 -
tryRelease 返回 false 却未改变 state:AQS 要求
tryRelease成功时返回 true,失败返回 false;若返回 false,AQS 会认为释放失败并抛异常。 - 在非 volatile 或非 CAS 场景修改 state:所有对 state 的修改必须通过 AQS 提供的原子方法,否则破坏可见性与原子性。
- 在同步器外部直接操作 AQS 方法:不要在 MyLock 外暴露 sync 对象或直接调用其 protected 方法,应严格通过 public 接口封装。
不复杂但容易忽略:AQS 是构建同步组件的“底盘”,真正难的是根据业务语义正确定义 state 含义、设计公平/非公平策略、处理超时、中断、取消等边界情况。从不可重入锁起步,逐步叠加功能,比一上来就写复杂同步器更稳妥。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











