28

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) {
    }
}

}

4

3 回答 3

15

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

在您的情况下,我们可以假设在您运行Executor的主线程处于暂停状态时,在运行中运行的线程获得焦点test(),尽管处于可运行状态。至于观察到的将提交的任务延迟两三秒之间的差异,我希望您会发现甚至三秒都不够的情况,这取决于其他进程在您的计算机上忙于做什么。

于 2010-12-04T00:48:05.030 回答
10

@seh 是对的。

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

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

然后就是Java GC 可能会导致所有其他线程阻塞的问题。

如果你真的需要 Java 的实时行为,它是可用的。例如:

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

于 2010-12-04T01:18:29.940 回答
9

我不得不说,我认为其他两个答案目前对 Java 并发类的看法不必要地低。它们不会为您提供毫秒精度(“真正的”实时应用程序所期望的),但它们通常做得很好。我使用 Futures 和 Executors 编写了大规模的商业服务,它们通常在预期时间的 10 毫秒内工作,即使在负载下也是如此。

我已经在带有 Java 1.6 的 MacOS 10.6 和带有 Java 1.6.0_22 的 WinXP 上运行了这个测试,它们都按预期工作。

我修改代码如下测试准确性:

    long time1 = System.nanoTime();

    System.out.println("Submitting");
    final Future<Object> future = exec.submit(call);
    try {
        future.get(1000, TimeUnit.MILLISECONDS);

        long time2 = System.nanoTime();
        System.out.println("No timeout after " + 
                             (time2-time1)/1000000000.0 + " seconds");

        fail("expected TimeoutException");
    } catch (TimeoutException ignore) {
        long time2 = System.nanoTime();
        System.out.println("Timed out after " +
                             (time2-time1)/1000000000.0 + " seconds");
    }
    finally {
        exec.shutdown();
    }

在 XP 中打印“1.002598934 秒后超时”,在 MacOS X 中打印“1.003158 秒后超时”。

如果原始发布者会描述他们的操作系统和 JDK 版本,也许我们可以确定这是否是一个特定的错误。

于 2010-12-04T02:01:32.787 回答