Java Выполнить задачу несколько раз - до истечения времени ожидания или выполнения условия - Синхронное выполнение - PullRequest
0 голосов
/ 08 февраля 2019

В настоящее время я сталкиваюсь с определенной проблемой, для которой я не знаю, как ее решить.Позвольте мне кратко описать эту проблему:

  • В методе, который я должен вызвать другой метод и обработать его ответ
  • Если этот ответ равен определенному значению, я продолжаю обычное выполнение
  • В противном случае, я жду x секунд (например, 2 секунды) и снова вызываю метод, чтобы снова обработать его ответ
  • Последний шаг повторяется до истечения определенного периода времени или пока метод не доставитпредполагаемый ответ.Таким образом, в целом, я не буду ждать вечно, а просто, например, максимум через 10 секунд, чтобы увидеть, возвращает ли метод ожидаемый ответ за это время.
  • Примечание: если для получения ожидаемого результата метод не требует 10 секундРегулярное выполнение должно продолжаться сразу после этого.Это означает, что я не хочу ждать 10 секунд, если результат есть, например, через 2 секунды.

Простое использование «старой школы» означает, что я нашел решение, такое как следующее (частично псевдокод для упрощения)

//No exception handling to simplify method
public ComplexValuePart mainMethod(int id) {
    //Other code executed before

    int maxAmountTries = 5;
    int waitTime = 2000;

    int counter = 0;
    boolean conditionFulfilled = false;
    ComplexValue cv = null;
    while (counter++ < maxAmountTries && !conditionFulfilled) {
        cv = calculatingMethod(id);
        if (cv.conditionHolds()) {
            conditionFulfilled = true;
        } else {
            Thread.sleep(waitTime);
        }
    }

    if (counter == maxAmountTries && !conditionFulfilled) {
        //report error
    }

    //Continue processing with cv
    return doThingsWithCV(cv);
}

public ComplexValue calculatingMethod(int id) {
    //Implementation not relevant here
}

Однако, используя Java 8 (сейчас это мое ограничение), я подумал, что могут быть другие / лучшие решения для этого?

Как альтернативаЯ что-то придумал, используя ScheduledExecutorService, например:

public void mainMethod(int id) {
    //Other code executed before
    ScheduledExecutorService service = Executors.newScheduledThreadPool(1);

    Future<?> future = service.scheduleAtFixedRate(new Runnable() {           
        @Override
        public void run() {
            ComplexValue cv = calculatingMethod(id);
            if (cv.conditionHolds()) {
                //shutdown service including awaitTermination
            }                
        }
    }, 0, 2, TimeUnit.SECONDS);

    try {
        future.get(20, TimeUnit.SECONDS);
    } catch (InterruptedException | ExecutionException | TimeoutException e) {
        //shutdown service including awaitTermination
    }

    //Continue processing with cv - How can I access cv here?
}

Для возврата ComplexValue Я думаю, мне нужно использовать Callable вместо Runnable?Могу ли я сделать это соответственно с Callable?Более того, он всегда заканчивался тайм-аутом, даже если условие выполнения сервиса было нормальным.В этом случае я не знаю, слишком ли это «накладные расходы» для реализации такой простой задачи.Каковы преимущества такого решения по сравнению с простым Thread sleep?

Есть ли какие-то функции Java 8, которые мне не хватает для реализации этой части?

Примечание: мне не нужнопараллельно выполнять различные задачи в этом цикле.Выполнение основного метода не должно продолжаться до тех пор, пока не истечет время ожидания или пока не будет достигнут желаемый результат - таким образом, async выполнения не будет.Метод должен вернуть некоторые данные на основе ответа от вызова службы.

Ответы [ 2 ]

0 голосов
/ 08 февраля 2019
final long deadline = System.currentTimeMillis() + TIMEOUT_MS;

boolean done;
Result result;
do {
  result = doSomething();
  done = resultIsOk(result);
  if ( !done ) {
    final long msRemaining = deadline - System.currentTimeMillis();
    if ( msRemaining > 0 ) {
      Thread.sleep(Math.min(msRemaining, RETRY_WAIT_TIME_MS);
    } else {
      done = true;
    }
  }
} while (!done);

if ( !resultIsOk(result) ) {
  // Error or something.
}

Этот код будет продолжать вызывать doSomething() до тех пор, пока не вернется ожидаемое значение или TIMEOUT_MS пройденных миллисекунд.Предполагая, что doSomething() возвращается быстро, это никогда не займет больше, чем TIMEOUT_MS (плюс, возможно, несколько миллисекунд).Кроме того, задержка между попытками является постоянной и не зависит от времени выполнения doSomething() (будет повторяться каждые 2 секунды, даже если doSomething() работает в течение 1,9 секунды), а наихудшее время, которое требуется, составляет TIMEOUT_MS + времяодин вызов doSomething() занимает.

Может быть полезно реализовать логику ожидания / тайм-аута в своем собственном классе Timeout.Затем вы можете создать экземпляр для заданного периода времени ожидания и передать его через разные уровни, чтобы даже самый нижний уровень (например, сетевой ввод-вывод) мог соответствующим образом установить значение времени ожидания, чтобы оно не превышало крайний срок, определенный верхним уровнем логики приложения.

Объедините этот класс Timeout с полиморфным RetryStrategies, и у вас будет очень гибкий способ задавать и обрабатывать множество различных сценариев.

Используя лямбды, вы даже можете создать полностью общую инкапсуляциюлогика полной повторной попытки, например

<R> R retryForResult( Supplier<R> functionToCall, Function<R,Boolean> resultValidationFunction, long maxTimeMs, Iterable<Long> retryDelays );  
0 голосов
/ 08 февраля 2019

Вместо этого я бы использовал TimerTask.Он повторяет выполнение метода run каждые миллисекунды waitTime.Вы можете указать, когда прекратить повторять эту задачу, вызвав "timer.cancel ()"

public void mainMethod(int id) {
    Timer timer = new Timer();

    timer.schedule(new TimerTask() {
        int counter = 0;
        @Override
        public void run() {
            cv = calculatingMethod(id);
            if (cv.conditionHolds() || counter++ > countLimit) {
                // conditionFulfilled = true;
                timer.cancel();
            }
        }
    }, 0, waitTime);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...