如何从 java 代理 stop/halt 主要 program/thread

How to stop/halt the main program/thread from a java agent

我有一个 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() 调用不影响测试 execution/jvm 当这个线程调用System.exit()时,测试线程仍然继续执行,就好像什么都没发生过一样。我想此时停止执行测试。

关于检测到超时时如何停止整个测试执行过程有什么建议吗?

OP 说我对安全经理的评论帮助他找到了根本原因,所以我将其转换为答案:

如文档所述,如果有安全管理器阻止,System.exit() 将不会关闭 JVM。不过,在那种情况下,您应该会看到 SecurityException

Gradle issue #11195 中的讨论提到了 Kafka 偶尔会意外退出的问题,并建议 Spring-Kafka 的安全管理器策略阻止它这样做。这是致力于 Spring-Kafka,但是 - 如果我理解正确的话 - 不是 Gradle.


另一个极端情况是关闭挂钩:调用 System.exit() 的线程会阻塞,直到 JVM 终止。如果关闭挂钩向该线程提交任务,则会导致死锁。