自定义锁卡死或抛异常的主因是未正确实现aqs契约:tryacquire必须原子返回true/false,state需按语义精确使用,shouldparkafterfailedacquire不可出错,且不可在其中抛异常或阻塞。

为什么你的自定义锁总在 tryAcquire 里卡死或抛 IllegalMonitorStateException
AQS 不是直接拿来用的工具类,它要求你严格遵守“状态 + 模式”契约。最常见问题:没重写 tryAcquire 却调用了 acquire,或者重写了但返回 false 后没处理线程阻塞逻辑,导致调用方无限等待。
关键点在于:AQS 自身不管理任何锁语义,tryAcquire 返回 true 才算获取成功;返回 false 时,它才会把当前线程包装成节点、入队、挂起——这个过程依赖你已正确实现 getState/compareAndSetState,且未被其他线程抢先修改状态。
-
tryAcquire必须是原子、无副作用的判断逻辑;不要在里面 sleep、IO 或调用可能阻塞的方法 - 如果你的同步器支持可重入(比如
ReentrantLock),tryAcquire要检查当前线程是否已是持有者,而不是只看state == 0 - 别在
tryAcquire里抛异常来“拒绝获取”,AQS 期望你用返回false表达竞争失败
AbstractQueuedSynchronizer 的 state 字段到底该怎么用
state 是 AQS 唯一的状态载体,int 类型,所有同步语义都靠它编码。它的含义完全由子类定义,AQS 只提供 getState、setState 和原子的 compareAndSetState。
典型误用:把 state 当作布尔标志(0/1)却忽略多线程下 CAS 失败重试,或把它当计数器却没处理溢出和负值边界。
立即学习“Java免费学习笔记(深入)”;
- 信号量(
Semaphore)用state表示剩余许可数,acquire做减法,release做加法 - 可重入锁(
ReentrantLock)用state记录重入次数,同时配合Thread变量记录当前持有者 - 如果需要多个维度状态(如读写锁),必须把多个字段压缩进一个
int,或改用long(需自行实现 CAS long 版本)
为什么 acquireInterruptibly 不响应中断,而 doAcquireInterruptibly 又会抛 InterruptedException
这不是 bug,是分层设计:上层 API(如 acquireInterruptibly)负责入口校验和中断传播,底层模板方法(如 doAcquireInterruptibly)才真正处理线程挂起与唤醒逻辑。一旦线程在队列中被挂起后收到中断,AQS 会清理节点并抛出异常,但前提是你的子类没有屏蔽或吞掉它。
常见坑:在 tryAcquire 中做了耗时操作,此时中断来了,但你没检查 Thread.interrupted(),导致中断状态丢失;或者你在 finally 块里调用了 selfInterrupt() 却忘了恢复中断标记。
- 所有带
Interruptibly后缀的方法,都要求你在进入等待前检查中断,且在被唤醒后重新抛出InterruptedException -
acquire系列方法不响应中断,适合内部不可中断场景;acquireInterruptibly应用于用户可取消的操作(如带超时的锁获取) - 不要在
tryAcquire里捕获InterruptedException并吞掉——那是 AQS 队列层的事
自定义同步器上线后 CPU 飙高,大概率是 shouldParkAfterFailedAcquire 实现错了
AQS 队列中节点的状态流转非常敏感。shouldParkAfterFailedAcquire 决定当前节点是否该挂起。如果它永远返回 false,线程就会在 acquire 循环里空转,吃满 CPU;如果它过早返回 true,又可能导致唤醒丢失,线程永久休眠。
标准实现里,它会先跳过所有 CANCELLED 节点,再把前驱设为 SIGNAL,最后返回 true。你不能跳过这步,也不能在没确保前驱能唤醒自己的前提下就挂起。
- 不要自己 new Node() 或修改
waitStatus后不触发唤醒逻辑 - 如果你的同步器支持“条件队列”(如
ConditionObject),signal必须把节点从条件队列移到同步队列,并显式设置前驱的waitStatus = SIGNAL - 调试时可用
Thread.dumpStack()查看线程是否卡在LockSupport.park或死循环里
真正难的不是写对几个方法,而是理解每个状态变更背后对应的线程生命周期和内存可见性约束。稍有偏差,就变成偶发超时、唤醒失效或静默死锁——这些不会报错,只会让系统在高并发下慢慢变脆。










