aqs设计哲学是同步语义由子类定义、调度逻辑由框架统一处理,tryacquire必须是纯函数式非阻塞判断,返回false后由aqs自动入队挂起;漏调unparksuccessor会导致等待线程永久阻塞;state需用cas安全操作,不可裸写;调试应优先使用hasqueuedthreads和isheldexclusively诊断。

为什么 tryAcquire 返回 false 就算“获取失败”,而不是抛异常?
因为 AQS 的设计哲学是「同步语义由子类定义,调度逻辑由框架统一处理」。tryAcquire 是纯粹的非阻塞判断:它只回答“此刻能不能拿”,不负责等待、唤醒或重试。返回 false 后,AQS 自动把当前线程包装成 Node 塞进 CLH 队列,挂起——这个决策权不在你手里。
常见错误是:在 tryAcquire 里写 Thread.sleep(10) 或调用 LockSupport.park(),这会破坏 AQS 的状态机,导致队列错乱或线程永远卡住。
-
tryAcquire必须是纯函数式:只读共享变量(如state)、不修改线程状态、不阻塞 - 如果需要「带超时的尝试」,应由上层调用
tryAcquireNanos,而非在tryAcquire内部实现 - 对可重入锁,需检查
Thread.currentThread() == getExclusiveOwnerThread(),再决定是否允许 state += 1
tryRelease 里忘记调用 unparkSuccessor 会怎样?
不会报错,但后续线程永远等不到唤醒信号。AQS 不会在 tryRelease 后自动 unpark,它只保证:如果你返回 true(表示完全释放),就去查队列头结点,调用 unparkSuccessor。漏掉这一步,等于关掉了“通知下游”的开关。
典型场景是实现一次性门闩(类似 CountDownLatch 的倒计时归零释放):state 减到 0 时必须确保唤醒所有等待者;但如果只改 state、没触发唤醒,那些线程就卡在 park 里,直到被中断或 JVM 终止。
- 只要
tryRelease返回true,就代表资源已彻底释放,此时应无条件让出调度权给队列首节点 - 不要在
tryRelease中做耗时操作(如 I/O、远程调用),它运行在持有锁的线程上下文中,会拖慢整个释放流程 - 若你的组件支持共享模式(如读写锁的读锁),则对应的是
tryReleaseShared,唤醒逻辑也不同:需调用doReleaseShared而非unparkSuccessor
state 字段不是“计数器”而是“状态位容器”,怎么安全拆解?
state 是一个 int,AQS 不规定它的业务含义。你可以把它当版本号、剩余许可数、读写标志组合、甚至高低位分别存不同信息——但所有读写都必须用 compareAndSetState 或 getState/setState 配合 volatile 语义,否则多线程下会丢失更新。
比如实现读写锁:高 16 位存读锁计数,低 16 位存写锁重入次数。错误做法是直接 state++;正确做法是用位运算提取、修改、CAS 回写:
int c = getState();
int w = exclusiveCount(c); // 低16位
if (w == 0 || getExclusiveOwnerThread() != current) {
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
if (compareAndSetState(c, c + acquires))
return true;
}
- 永远避免裸写
state = ...,必须用 CAS 或 synchronized 块保护(但后者违背 AQS 无锁设计初衷) - 注意
exclusiveCount和sharedCount这两个静态工具方法,它们是 JDK 源码里已有的位运算封装,别自己手写移位逻辑 - 如果业务需要 > 2^16 的计数值,要么换
long(需继承AbstractQueuedLongSynchronizer),要么用外部原子变量间接映射
调试时看不到线程阻塞在哪?先查 isHeldExclusively 和 hasQueuedThreads
AQS 自身不暴露内部队列结构,但提供了几个关键诊断方法。当你发现线程没拿到锁又没抛异常,最该查的是:hasQueuedThreads() 返回 true 说明真在排队;isHeldExclusively() 返回 false 表示当前没线程独占——这两者结合,能快速定位是“还没排到”还是“根本没进队”。
容易被忽略的坑是:自定义同步器忘了重写 isHeldExclusively。默认实现永远返回 false,导致 ConditionObject 的 await 判断失败,抛出 IllegalMonitorStateException,而你以为是锁没加成功。
- 只要支持独占模式,就必须重写
isHeldExclusively,返回getExclusiveOwnerThread() == Thread.currentThread() -
getQueueLength()只统计非 head 的等待节点,head 是虚拟节点,不算在内 - 生产环境慎用
toString()打印 AQS 状态——它会遍历整个队列,可能引发长暂停
return 值、漏一次 unpark、或者在 tryAcquire 里做了不该做的事,问题都不会立刻爆发,而是在线程调度路径上某个偶然时机突然卡死或吞掉唤醒信号——这种延迟性,才是最要命的。










