为什么 Java Future.get(timeout) 不可靠?

Future.get(timeout) 在给定的超时后不会可靠地抛出 TimeoutException。这是正常行为还是我可以做些什么来使其更可靠?此测试在我的计算机上失败。但是,如果我睡了3000而不是2000,它就会过去。

public class FutureTimeoutTest {
@Test
public void test() throws
    ExecutionException,
    InterruptedException {

    ExecutorService exec = Executors.newSingleThreadExecutor();
    final Callable call = new Callable() {
        @Override
        public Object call() throws Exception {
             try {
                Thread.sleep(2000);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
            return 0;
        }
    };
    final Future future = exec.submit(call);
    try {
        future.get(1000, TimeUnit.MILLISECONDS);
        fail("expected TimeoutException");
    } catch (TimeoutException ignore) {
    }
}

}


答案 1

没有理由期望测试通过。假设您提交任务以执行,然后等待其完成,则在等待开始之前可能会经过任何时间,从而使任务有足够的时间耗尽睡眠持续时间并完成。Future#get()

在你的例子中,我们可以假设在内运行的线程获得焦点,而你的主线程处于暂停状态,尽管处于可运行状态。至于观察到的将提交的任务停滞两秒和三秒钟之间的差异,我希望您可以找到甚至三秒钟都不够的情况,具体取决于计算机上其他进程忙于执行的操作。Executortest()


答案 2

@seh是对的。

您期待Java通常所说的“实时”行为。除非您在实时操作系统上运行的具有实时功能的 Java 发行版中使用实时库,否则无法可靠地实现这一点。

只是为了说明,像HotSpot这样的现代JVM中的Java线程实现依赖于主机操作系统的本机线程调度程序来决定何时运行哪些线程。除非线程调度程序特别了解实时截止时间和内容,否则在决定何时运行哪些线程时,它可能会采取“整个系统”的视图。如果系统已加载,则任何特定线程都可能无法安排运行几秒钟...或更长...在阻止其运行的条件(例如,等待计时器事件)通过后。

然后存在一个问题,即Java GC可能导致所有其他线程阻塞。

如果您确实需要 Java 的实时行为,则可以使用它。例如:

但是,您应该期望更改应用程序以使用不同的 API 来提供实时行为。


推荐