Запланированный исполнитель: опросить результат с фиксированной скоростью и выйти, если тайм-аут или результат действителен
Проблема: у меня есть требование вызывать метод 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 и получить результат. Мне пришлось использовать трех исполнителей для следующих целей:
- исполнитель для запуска задачи с фиксированным интервалом
- исполнитель, чтобы остановить все, когда время истекло
- исполнитель, чтобы остановить все, если действительный результат получен до истечения времени ожидания.
Может кто-нибудь предложить лучший подход, это кажется сложным для такой тривиальной задачи?
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
потому что задача либо выполнена успешно, либо истекло время ожидания.