问题描述
我有一个 gradle 测试任务,它运行给定文件中的测试列表。 有时,任何特定的测试执行都会卡住,不会继续执行列表中的下一个测试。
为此,我尝试添加一个 java 代理,它将检测每个测试执行中的超时并在这种情况下调用 System.exit()。 (我知道调用 System.exit() 似乎是一个草率的决定,但抛出异常似乎并没有停止测试执行) Java 代理使用字节伙伴建议来执行此操作。
public class TimerAdvice {
public static CountDownLatch latch;
@Advice.OnMethodEnter
static long enter(@Advice.This Object thisObject,@Advice.Origin String origin,@Advice.Origin("#t #m") String detaildOrigin) throws InterruptedException {
System.out.println("Timer Advice Enter thread: " + Thread.currentThread().getName() + " time: " + Instant.Now().toString());
latch = new CountDownLatch(1);
ThreadFactory factory = new MyThreadFactory(new MyExceptionHandler());
ExecutorService threadPool = Executors.newFixedThreadPool(1,factory);
threadPool.execute(new TestCallable());
return System.nanoTime();
}
@Advice.OnMethodExit (onThrowable = Throwable.class)
static void onExit(@Advice.Origin Method method) throws ClassNotFoundException,illegalaccessexception,InstantiationException {
System.out.println("Timer Advice Exit thread: " + Thread.currentThread().getName() + " time: " + Instant.Now().toString());
System.out.println("Counting down");
latch.countDown();
}
}
public class TestCallable implements Runnable {
@Override
public void run() {
try {
latch.await(10,TimeUnit.MINUTES);
} catch (InterruptedException e) {
e.printstacktrace();
throw new IllegalStateException(e.getMessage());
}
if(latch.getCount() > 0) {
System.err.println("Callable thread"
+ Thread.currentThread().getName() +
"TIMEOUT OCCURED!!!!");
System.exit(1);
}
}
}
latch countDown() 方法将被处理 OnExit 通知的方法调用。 在此之前,线程将等待指定的超时时间。
我的问题是,为什么 System.exit() 调用不影响测试执行/jvm 当这个线程调用 System.exit() 时,测试线程仍然继续执行,就好像什么都没发生过一样。我想在此时停止执行测试。
当检测到超时时我应该如何停止整个测试执行过程有什么建议吗?
解决方法
OP 说我对安全经理的评论帮助他找到了根本原因,所以我将其转换为答案:
如文档所述,System.exit()
不会关闭 JVM,如果有安全管理器阻止它这样做。在这种情况下,您应该会看到一个 SecurityException
。
Gradle issue #11195 中的一个讨论提到了 Kafka 偶尔意外退出的问题,并建议 Spring-Kafka 的安全管理器策略阻止它这样做。这是对 Spring-Kafka 的承诺,但是 - 如果我理解正确的话 - 不是 Gradle。
另一个极端情况是关闭钩子:调用 System.exit()
的线程会阻塞,直到 JVM 终止。如果关闭钩子向该线程提交任务,则会导致死锁。