gpt4 book ai didi

java - 为什么 JUnit 使用 CountDownLatch 来实现 FailOnTimeout

转载 作者:行者123 更新时间:2023-11-30 07:00:23 25 4
gpt4 key购买 nike

我刚开始阅读 JUnit 4.13 ( https://github.com/junit-team/junit ) 的代码,对 org.junit.internal.runners.statements.FailOnTimeout 的实现有点困惑:

@Override
public void evaluate() throws Throwable {
CallableStatement callable = new CallableStatement();
FutureTask<Throwable> task = new FutureTask<Throwable>(callable);
ThreadGroup threadGroup = new ThreadGroup("FailOnTimeoutGroup");
Thread thread = new Thread(threadGroup, task, "Time-limited test");
thread.setDaemon(true);
thread.start();
callable.awaitStarted();
Throwable throwable = getResult(task, thread);
if (throwable != null) {
throw throwable;
}
}

/**
* Wait for the test task, returning the exception thrown by the test if the
* test failed, an exception indicating a timeout if the test timed out, or
* {@code null} if the test passed.
*/
private Throwable getResult(FutureTask<Throwable> task, Thread thread) {
try {
if (timeout > 0) {
return task.get(timeout, timeUnit); // HERE limits the time
} else {
return task.get();
}
} catch (InterruptedException e) {
return e; // caller will re-throw; no need to call Thread.interrupt()
} catch (ExecutionException e) {
// test failed; have caller re-throw the exception thrown by the test
return e.getCause();
} catch (TimeoutException e) {
return createTimeoutException(thread);
}
}

CallableStatement 是:

private class CallableStatement implements Callable<Throwable> {
private final CountDownLatch startLatch = new CountDownLatch(1);

public Throwable call() throws Exception {
try {
startLatch.countDown();
originalStatement.evaluate(); // HERE the test runs
} catch (Exception e) {
throw e;
} catch (Throwable e) {
return e;
}
return null;
}

public void awaitStarted() throws InterruptedException {
startLatch.await();
}
}

这是我对代码的理解:

evaluate() 为测试方法启动一个新线程。 callable.awaitStarted() block evaluate() 直到 startLatch.countDown(),然后是 getResult() 次测试方法。

这是我的问题:

  • 为什么 thread(在 evaluate() 中)应该是守护线程?
  • CountDownLatch 是否仅用于阻塞 getResult() 直到 thread 运行?它真的有效吗(我认为没有什么可以避免 callable.awaitStarted()getResult() 之间的上下文切换)?有没有“更简单”的方法来做到这一点?

我对并发不是很熟悉。如果有人能解释这些或指出我的错误,我将不胜感激。谢谢。


关于第二个问题的更多解释:

我会将这两个线程表示为“evaluate() 线程”和“CallableStatement 线程”。

我认为当 callable.awaitStarted() 执行到 startLatch.countDown() 完成时,“evaluate() 线程”被阻塞,但测试方法可能会启动在上下文切换回“评估()线程”之前运行。 “evaluate()线程”再次被唤醒后,立即调用FutureTask.get(),会阻塞“evaluate()线程”,但不能保证“CallableStatement线程”被正确唤醒之后。

所以,我认为测试方法开始的时刻与 task.get(timeout, timeUnit) 被调用的时刻无关。如果有很多其他线程,则它们之间的时间间隔可以忽略不计。

最佳答案

Why thread (in evaluate()) should be a daemon thread?

如果测试有任何问题,它允许 JVM 正确退出。另见 What is Daemon thread in Java?

Is CountDownLatch just used for blocking the getResult() until thread is running? Does it really work (I thought nothing can avert a context switch between callable.awaitStarted() and getResult())? Is there any "simpler" way to do this?

没有什么可以避免上下文切换,但是如果 thread 已经启动并正在运行,它最终将获得一些 CPU 注意力并且 originalStatement.evaluate() 将被执行。这样做的原因是可能没有可用的底层操作系统线程,然后测试的测试执行可能会失败,尽管测试本身是正确的。还有其他方法可以做到这一点,例如Semaphore,但是 CountDownLatch 非常高效,而且便宜可以做到这一点。

关于java - 为什么 JUnit 使用 CountDownLatch 来实现 FailOnTimeout,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30638224/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com