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

Andrew Raphael picture Andrew Raphael · Dec 4, 2010 · Viewed 22.8k times · Source

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.

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

}

Answer

seh picture seh · Dec 4, 2010

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.

In your case, we can assume that the thread running within the Executor gets 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.