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

And*_*ael 27 java multithreading timeout java.util.concurrent

在给定的超时后,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) {
    }
}
Run Code Online (Sandbox Code Playgroud)

}

seh*_*seh 15

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

在您的情况下,我们可以假设在Executor主要线程运行期间运行的线程获得焦点test(),尽管处于可运行状态.至于在提交的任务停止两秒和三秒之间观察到的区别,我希望您可以找到甚至三秒钟不足的情况,具体取决于您的计算机上正忙于执行的其他进程.


Ste*_*n C 10

@seh是对的.

您期望从Java中通常称为"实时"行为.除非您在实时操作系统上运行的实时Java分发中使用实时库,否则无法可靠地实现这一点.

为了说明,像HotSpot这样的现代JVM中的Java线程实现依赖于主机操作系统的本机线程调度程序来决定运行什么线程.除非线程调度程序特别了解实时截止日期和内容,否则在决定运行什么线程时可能会采用"整个系统"视图.如果系统已加载,则任何特定线程可能无法安排运行数秒......或更长时间......在阻止其运行的条件(例如,等待计时器事件)已经过去之后.

然后存在Java GC可能导致所有其他线程阻塞的问题.

如果您确实需要Java的实时行为,那么它是可用的.例如:

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


and*_*wmu 8

我不得不说,我认为其他两个答案目前对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();
    }
Run Code Online (Sandbox Code Playgroud)

在XP中打印"在1.002598934秒后超时",在MacOS X中打印"在1.003158秒后超时".

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