Запланированный исполнитель: опросить результат с фиксированной скоростью и выйти, если тайм-аут или результат действителен

Проблема: у меня есть требование вызывать метод dao с фиксированной скоростью, скажем, каждые 10 секунд, затем мне нужно проверить, верен ли результат, если да, выйдите, иначе продолжайте вызывать этот метод каждые 10 секунд, пока я не получу действительный результат или не определю время ожидания (скажем, 2 минуты) прошло.

Подходы: я хочу разделить логику задачи и планировщика и написать задачу таким образом, чтобы она могла использоваться различными классами, имеющими схожие требования.

Один способ, которым я могу думать, состоит в том, чтобы определить новую задачу опроса

public abstract class PollerTask<T> implements Runnable {

    abstract public boolean isValid(T result);

    abstract public T task();

    private T result;

    private volatile boolean complete;

    public boolean isComplete() {
        return complete;
    }

    public T getResult() {
        return result;
    }

    @Override
    final public void run() {
        result = task();
        if (complete = isValid(result)) {
            //may be stop scheduler ??
        }

    }
}

Пользователю нужно просто обеспечить выполнение задачи и isValid;

Затем мы можем определить отдельный класс, который использует пул freq и timeout, создает запланированного исполнителя и отправляет эту задачу.

public class PollerTaskExecutor {

    private int pollingFreq;
    private int timeout;
    private ScheduledExecutorService executor;
    private ScheduledExecutorService terminator;
    private ExecutorService condition;
    private volatile boolean done;
    private ScheduledFuture future;

    public PollerTaskExecutor(int pollingFreq, int timeout) {
        this.pollingFreq = pollingFreq;
        this.timeout = timeout;
        executor = Executors.newSingleThreadScheduledExecutor();
        terminator = Executors.newSingleThreadScheduledExecutor();
        condition = Executors.newSingleThreadExecutor();
    }

    public void submitTaskForPolling(final PollerTask pollerTask) {
        future = executor.scheduleAtFixedRate(pollerTask, 0, pollingFreq, TimeUnit.SECONDS);
        terminator.schedule(new Runnable() {
            @Override
            public void run() {
                complete();
            }
        }, timeout, TimeUnit.SECONDS);
        condition.execute(new Runnable() {
            @Override
            public void run() {
                if (pollerTask.isComplete()) {
                    complete();
                }
            }
        });

    }

    public boolean isDone() {
        return done;
    }

    public void complete() {
        future.cancel(false);
        executor.shutdown();
        terminator.shutdown();
        condition.shutdown();
        done = true;

    }

Теперь пользователь может подождать, пока pollerExecutor.isDone вернет true и получить результат. Мне пришлось использовать трех исполнителей для следующих целей:

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

Может кто-нибудь предложить лучший подход, это кажется сложным для такой тривиальной задачи?

2 ответа

Решение

Сделайте это самостоятельной задачей. В псевдокоде:

public class PollingTaskRunner {

...
CountDownLatch doneWait = new CountDownLatch(1);
volatile boolean done;

PollingTaskRunner(Runnable pollingTask, int frequency, int period) {
    ...
    endTime = now + period;
    executor.schedule(this, 0);
}

run() {

    try {
        pollingTask.run();
    } catch (Exception e) {
        ...
    }
    if (pollingTask.isComplete() || now + frequency > endTime) {
        done = true;
        doneWait.countDown();
        executor.shutdown();
    } else {
        executor.schedule(this, frequency);
    }
}

await() {
    doneWait.await();
}

isDone() {
    return done;
}
}

Это не так сложно, но добавьте множество операторов отладки при первом запуске / тестировании, чтобы вы знали, что происходит. После того, как он работает, как задумано, легко повторно использовать шаблон.

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

Еще проще Есть PollerTask поместите его результат в BlockingQueue, Тогда есть PollingTaskRunner сделать время poll на что BlockingQueue, Всякий раз, когда контроль возвращается из poll вызов ScheduledFuture.cancel потому что задача либо выполнена успешно, либо истекло время ожидания.

Другие вопросы по тегам