tryLock(long, TimeUnit)在超时后正确返回false:需主线程持锁不放,子线程调用该方法并断言返回false,配合CountDownLatch精确时序或sleep确保超时发生,避免调度干扰。

要验证 ReentrantLock 的超时获取特性(即 tryLock(long timeout, TimeUnit unit) 在指定时间内未获取到锁时返回 false),单元测试需构造**另一个线程已持有锁且不释放**的场景,再让被测线程尝试带超时地获取锁,并断言其在超时后正确返回 false。关键在于控制锁的持有时间、确保竞争真实发生、避免因调度不确定性导致误判。
1. 基础验证:主线程持锁,子线程尝试超时获取
这是最直接的方式:主线程先获取锁并 sleep 一段时间(长于超时时间),子线程调用 tryLock(100, MILLISECONDS),预期返回 false。
- 主线程调用
lock.lock()后立即启动子线程 - 子线程中调用
lock.tryLock(100, MILLISECONDS),断言结果为false - 主线程 sleep 超过 100ms 后再 unlock,确保子线程一定超时
- 注意:子线程需显式
join()或用CountDownLatch同步,避免主线程提前结束
2. 使用 CountDownLatch 精确控制时序
避免依赖 Thread.sleep() 的不稳定性,用 CountDownLatch 协调锁的持有与尝试时机:
- 主线程获取锁后,调用
latch.countDown()通知子线程可以开始 tryLock - 子线程 await latch,然后立即调用
tryLock(50, MILLISECONDS) - 主线程在 await 之后 sleep 100ms 再 unlock,确保子线程必超时
- 这样能排除“子线程启动慢导致实际等待时间不足”的干扰
3. 验证成功获取场景(边界检查)
除了超时失败,也要覆盖“刚好来得及获取”的情况,增强测试完备性:
- 主线程获取锁后只 sleep 比超时时间短(如超时设为 200ms,sleep 100ms)
- 子线程在主线程 unlock 后应能成功获取锁,
tryLock返回true - 可配合
assertTrue(lock.isHeldByCurrentThread())进一步确认锁状态
4. 注意事项与常见陷阱
这类测试容易因并发细节出错,需特别留意:
-
不要在测试中使用
Thread.sleep(0)或极短时间(如 1ms)作为超时值 —— 受系统时钟精度和线程调度影响,可能偶然成功,导致测试不稳定 - 确保锁对象是同一个实例,且未被其他测试干扰(建议每个测试 new 一个独立
ReentrantLock) - 子线程若获取锁成功,必须主动 unlock,否则会泄漏锁(可在
finally块中处理) - JVM 参数或 CI 环境负载可能导致调度延迟,超时值建议设为 50ms 以上,留出安全余量
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











