Почему Java Future.get (timeout) не надежен? - PullRequest
26 голосов
/ 04 декабря 2010

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

}

Ответы [ 3 ]

15 голосов
/ 04 декабря 2010

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

11 голосов
/ 04 декабря 2010

@ seh прав.

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

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

ТогдаСуществует проблема, заключающаяся в том, что Java GC может вызвать блокировку всех других потоков.

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

  • * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *.1019 *

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

7 голосов
/ 04 декабря 2010

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

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

...