如何从 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 终止。如果关闭挂钩向该线程提交任务,则会导致死锁。
我有一个 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 终止。如果关闭挂钩向该线程提交任务,则会导致死锁。