Подтвердить что ты не робот

Почему 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) {
    }
}

}

4b9b3361

Ответ 1

Нет причин ожидать, что тест пройдет. Учитывая, что вы отправляете задачу на выполнение, а затем ждите ее завершения, любое количество времени может пройти до того, как начнется ваше ожидание на Future#get(), что позволит задаче много времени для исчерпания продолжительности сна и завершения.

В вашем случае мы можем предположить, что поток, выполняющийся внутри Executor, получает фокус, пока ваш основной поток, проходящий через test(), не работает, несмотря на то, что он находится в состоянии runnable. Что касается наблюдаемой разницы между остановкой поданной задачи в течение двух и трех секунд, я ожидаю, что вы найдете ситуации, когда даже три секунды недостаточно, в зависимости от того, что другие процессы заняты на вашем компьютере.

Ответ 2

@seh является правильным.

Вы ожидаете, что обычно принято называть "в реальном времени" с Java. Этого нельзя добиться надежно, если вы не используете библиотеки реального времени в режиме реального времени, поддерживающем Java-дистрибутив, работающий в операционной системе реального времени.

Чтобы проиллюстрировать, реализация потоков Java в современных JVM, таких как HotSpot, зависит от планировщика собственных потоков хост-системы, чтобы решить, какие потоки будут выполняться, когда. Если планировщик потоков не знает о сроках и т.д. В реальном времени, он может воспринимать представление "всей системы" при определении того, какие потоки будут выполняться, когда. Если система загружена, любой конкретный поток не может быть запланирован для запуска в течение нескольких секунд... или дольше... после того, как условия, которые препятствовали его запуску (например, ожидание события таймера), прошли.

Тогда возникает проблема, что Java GC может блокировать все остальные потоки.

Если вам действительно нужно поведение в реальном времени с Java, оно доступно. Например:

Однако вы должны ожидать, что ваши приложения будут использовать разные API-интерфейсы, чтобы дать вам поведение в режиме реального времени.

Ответ 3

Я должен сказать, я думаю, что два других ответа в настоящее время имеют неоправданно низкое мнение о классах Java concurrency. Они не дадут вам миллисекундной точности (чего ожидают "настоящие" приложения в режиме реального времени), но они довольно хорошо работают. Я написал крупномасштабные коммерческие услуги с использованием фьючерсов и исполнителей, и они обычно работали в течение 10 миллисекунд ожидаемого времени, даже при загрузке.

Я проверил этот тест как на MacOS 10.6 с Java 1.6, так и на WinXP w/Java 1.6.0_22, и оба они работают должным образом.

Я изменил код следующим образом, чтобы проверить точность:

    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, возможно, мы могли бы определить, является ли это определенной ошибкой.