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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4350941/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 16:23:42  来源:igfitidea点击:

Why is Java Future.get(timeout) Not Reliable?

javamultithreadingtimeoutjava.util.concurrent

提问by Andrew Raphael

Future.get(timeout) does not reliably throw the TimeoutException after the given timeout. Is this normal behavior or can I do something to make this more reliable? This test fails on my machine. However if I sleep for 3000 instead of 2000, it will pass.

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

}

}

回答by seh

There is no reason to expect the test to pass. Given that you submit the task for execution and then wait on its completion, any amount of time could pass before your wait on Future#get()begins, allowing the task plenty of time to exhaust the sleep duration and complete.

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

In your case, we can assume that the thread running within the Executorgets focus while your main thread running through test()is on hold, despite being in a runnable state. As for the observed difference between stalling the submitted task for two and three seconds, I expect you could find situations where even three seconds is insufficient, depending on what other processes are busy doing on your computer.

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

回答by Stephen C

@seh is right.

@seh 是对的。

You are expecting what is commonly called "real-time" behavior from Java. This cannot be achieved reliably unless you use real-time libraries in a real-time capable Java distribution running on a real-time operating system.

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

Just to illustrate, the Java thread implementation in modern JVMs like HotSpot relies on the host operating system's native thread scheduler to decide what threads to run when. Unless the thread scheduler is specifically aware of real-time deadlines and stuff, it is likely to take a "whole of system" view when deciding what threads to run when. If the system is loaded, any particular thread may not get scheduled to run for seconds ... or longer ... after the conditions that prevented it running (e.g. waiting for a timer event) have passed.

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

Then there is the problem that the Java GC may cause all other threads to block.

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

If you really need real-time behavior from Java, it is available. For example:

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

However, you should expect to change your applications to use different APIs to give you real-time behavior.

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

回答by andrewmu

I have to say, I think the other two answers currently have an unnecessarily low opinion of the Java concurrency classes. They will not give you millisecond accuracy (what "real" real-time applications expect) but they do quite well usually. I've written large scale commercial services using Futures and Executors and they normally worked within 10 milliseconds of the expected times, even under load.

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

I've run this test both on MacOS 10.6 with Java 1.6 and WinXP w/ Java 1.6.0_22 and both of them work as expected.

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

I modified the code as follows to test the accuracy:

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

    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();
    }

In XP this prints "timed out after 1.002598934 seconds" and in MacOS X it prints "timed out after 1.003158 seconds".

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

If the original poster would describe their OS and JDK version, perhaps we could determine if this is a particular bug.

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