Java Multi-Threading. Можно ли завершать поток и получать значение по условию, а в противном случае повторять вызов?

Название может сбить с толку, потому что вопрос не тривиальный и я не могу кратко его описать.

Представим есть класс имитирующий ответ от микросервиса:

public class MicroserviceCallEmulator implements CallEmulator {

    private static final List<String> VALUES = Arrays.asList("A", "B", "C");

    @Override
    public String doCall() {
        try {
            Thread.sleep(ThreadLocalRandom.current().nextInt(1000));
        } catch (InterruptedException e) { throw new RuntimeException(e); }

        return VALUES.get(ThreadLocalRandom.current().nextInt(3));
    }
}

В нём есть метод doCall(), который имитирует задержку а потом возвращает одно из значений списка VALUES. И есть класс использующий этот имитатор:

public class MicroserviceCallProvoker implements CallProvoker {

    private final CallEmulator callEmulator;

    public MicroserviceCallProvoker(CallEmulator callEmulator) {
        this.callEmulator = callEmulator;
    }

    @Override
    public void doSomething() {
        callEmulator.doCall();
    }
}

В методе doSomething() нужно реализовать следующую логику:

  1. Вызов doCall() должен быть асинхронным. То есть doSomething() должен просто отправлять запрос и ничего не ждать и не возвращать.
  2. Значения A и B ответе от doCall() являются не подходящими. Вызовы нужно продолжать асинхронно до тех пор, пока от doCall() не вернёться C
  3. Вызовы doCall() после непдоходящего ответа необходимо повторно посылать НЕ сразу же, а по истечению 10 секунд
  4. Есть предел по времени. Повторные вызовы doCall() должны быть ограничены по времени. К примеру, doCall() может 1000 раз вернуть строку Aи это займёт 1000 секунд. Такое поведение не подходит. Мы должны пытаться вызывать его в течении 400 секунд и потом (даже если не получили заветную строку C) прекращать выполнение асинхронной задачи.

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


Ответы (1 шт):

Автор решения: Roman-Stop RU aggression in UA

Сначала всю логику повтороных попыток реализовываем в отдельной функции:

void doCallWithRetry() {
    long endTime = System.currentTimeMillis() + 
          TimeUnit.MILLISECONDS.convert(400, TimeUnit.SECONDS);
    while (System.currentTimeMillis() <= endTime) {
         String result = callEmulator.doCall();
         if (result.equal("C")) {
            break;
         }
         TimeUnit.SECONDS.sleep(10);
    }    
}

И теперь запускаем doCallWithRetry в отдельном потоке из doSomething (в примере использую ExecutorService, но можно любой другой способ запуска в отдельном потоке):

void doSomething() {
   executorService.execute(this::doCallWithRetry);
}
→ Ссылка