AQS自定义同步器验证需覆盖线程安全、状态原子性、等待/唤醒、中断响应和超时行为;通过白盒探针+黑盒并发测试,确保state可见性、CLH队列行为、中断/超时契约及组合压力下状态一致性。

验证 AQS(AbstractQueuedSynchronizer)自定义同步器的正确性,不能只测“功能跑通”,而要重点覆盖 线程安全、状态变更原子性、等待/唤醒逻辑、中断响应、超时行为 等核心契约。单元测试需结合白盒逻辑 + 黑盒并发场景,下面给出可落地的验证策略和关键示例。
覆盖核心状态流转与线程可见性
AQS 的 state 变更必须严格遵循 volatile 语义和 CAS 原子性。测试不能只断言最终结果,还要检查中间状态是否被正确发布:
- 用
AtomicInteger模拟多线程对state的并发修改,在同步器内部插入探针(如回调钩子或包装 state 读写),验证每次 CAS 成功后,其他线程能立即看到新值 - 测试 tryAcquire/tryRelease 返回 false 时,
state不应被意外修改(例如在条件不满足时仍执行了 state++) - 对可重入锁类同步器,验证同一线程多次 acquire 后,state 累加值与重入次数一致;release 时逐次递减,且仅在最后一次才真正释放锁
并发竞争下的队列行为验证
AQS 的 CLH 队列是其调度基础。虽不建议直接断言 Node 字段(属实现细节),但可通过可观测行为间接验证:
- 启动 N 个线程争抢独占锁(N > 1),用
CountDownLatch控制同时触发,再用Thread.sleep(10)确保至少部分线程入队;随后调用hasQueuedThreads()断言返回 true - 让一个线程持锁并阻塞,另起线程调用
acquireInterruptibly()进入等待;再中断该线程,验证其抛出InterruptedException且从队列中移除(可通过getQueueLength()降为 0 辅助判断) - 对支持超时的 acquire 方法(如
tryAcquireNanos),设置极短超时(如 1ns),验证返回 false 且线程未留在队列中
中断与超时的契约一致性
AQS 要求:可中断获取必须响应中断,超时获取必须在截止前放弃。这是易出错点:
- 写一个死循环线程反复调用
acquireInterruptibly(),主线程在 50ms 后中断它;断言捕获到InterruptedException,且同步器最终处于一致状态(如无残留 Node、state 合理) - 对
tryAcquireNanos(long),传入System.nanoTime() - 1作为 deadline,确保立即超时;验证返回 false,且不改变 state、不新增队列节点 - 特别注意:中断发生时若线程刚完成 CAS 但尚未设置 waitStatus,需确保不会导致“假唤醒”或状态不一致 —— 可通过在 acquire 流程关键点插入
Thread.interrupted()检查并日志辅助定位
组合边界场景的压力验证
单一测试易遗漏竞态,需设计复合压力用例:
-
混合操作压测:10 个线程交替执行 acquire/release/tryAcquireNanos(1)/acquireInterruptibly,运行 5 秒,最后验证 state == 0 且无等待线程(
getQueueLength() == 0) -
虚假唤醒防御:重写
isHeldExclusively()返回随机 true/false,配合循环 await,在 condition 上做并发 signal + interrupt,确认线程不会因虚假唤醒跳过条件检查 -
回收安全性:对支持取消的同步器(如 FutureTask),触发 cancel(true),验证对应 Node 的
waitStatus被设为CANCELLED,且后续 acquire 不会将其唤醒
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











