junit 5 提供 asserttimeout 和 asserttimeoutpreemptively 实现超时控制,前者不中断线程,后者尝试中断;异步测试优先用 completablefuture.ortimeout 配合 assertthrows;禁用手动 sleep 轮询,应使用同步工具协调并确保 executorservice 正确关闭;配合 @resourcelock 隔离共享资源预防死锁。

JUnit 5 提供了原生支持超时测试的机制,能有效防止因死锁、无限等待或阻塞操作导致的测试卡住。关键不是“测出死锁”,而是让测试本身具备防御性——一旦被测逻辑陷入不响应状态,测试主动失败,不拖垮整个测试套件。
用 assertTimeout 或 assertTimeoutPreemptively 强制设限
这两个静态方法是 JUnit 5 中最直接的超时控制手段:
- assertTimeout:在指定时间内执行代码块,超时后抛出异常并结束测试;但不会中断正在运行的线程(适合纯计算类阻塞)
- assertTimeoutPreemptively:更激进——超时时会尝试中断执行线程(适用于 I/O、wait()、Lock.lock() 等可响应中断的操作)
示例:
@Test
void testPaymentUpdateWithTimeout() {
// 防止数据库 update 因行锁/间隙锁陷入死锁等待
assertTimeoutPreemptively(Duration.ofSeconds(3), () -> {
paymentService.updatePaymentStatusByLimit(); // 可能触发 DB 死锁场景
});
}
对 CompletableFuture 或异步逻辑加超时
若被测方法返回 CompletableFuture,优先用其自带的超时能力,再配合断言:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 调用 orTimeout():超时后返回 CompletionException
- 搭配 assertThrows(CompletionException.class, ...) 验证是否如期失败
示例:
@Test
void testAsyncOrderProcessingTimesOut() {
CompletableFuture<string> future = orderService.processAsync()
.orTimeout(2, TimeUnit.SECONDS);
assertThrows(CompletionException.class, () -> future.join());
}</string>
避免在测试中自己 sleep 或轮询等“伪超时”
手写 while + Thread.sleep 检查状态不仅不可靠,还掩盖真实问题。正确做法是:
- 不依赖时间等待,除非你明确在验证超时行为本身
- 用 CountDownLatch / CyclicBarrier 等同步工具显式协调线程完成,再断言结果
- 所有 ExecutorService 必须 shutdown() + awaitTermination(),否则未终止线程可能阻塞 JVM 退出
结合资源锁预防并发测试中的隐性死锁
当多个测试并发修改同一数据库表或共享文件时,容易因事务隔离级别或锁顺序引发死锁。此时可配合 @ResourceLock 注解隔离资源:
-
@ResourceLock(value = "DB_PAYMENT_TABLE", mode = ResourceMode.READ_WRITE):确保同一时刻只有一个测试操作该表 - READ 模式允许多个读测试并行,WRITE 模式强制串行化写操作
这从源头降低多测试并发触发 DB 层死锁的概率,比事后靠超时更治本。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










