CompletableFuture с PriorityBlockingQueue - PullRequest
2 голосов
/ 23 февраля 2020

Я пытаюсь написать однопоточный исполнитель, который возвращает CompletableFuture, когда задача запланирована и выполняет задачу на основе PriorityBlockingQueue.

Мои задачи выглядят так:

  public interface DriverTask<V> {

    V call(WebDriver driver) throws Throwable;

    default TaskPriority getPriority() {
      return TaskPriority.LOW;
    }
  }

  public enum TaskPriority {
    HIGH,
    MEDIUM,
    LOW
  }

Теперь моя проблема в том, что когда я использую метод CompletableFuture.supplyAsync, Исполнитель получает только Runnable, и я не знаю, как заставить моего Исполнителя узнать приоритет исходной Задачи.

Есть ли другой способ создать CompletableFuture, чтобы я мог выполнить их в соответствии с приоритетом?

1 Ответ

2 голосов
/ 24 февраля 2020

Принцип таких методов, как supplyAsync, заключается в создании нового экземпляра CompletableFuture для настройки и асинхронного задания, которое в конечном итоге complete приведет к будущему.

Вы можете сделать то же самое для вашей нетривиальной установки :

private static WebDriver currentDriver() {
    …
}
private static final ExecutorService BACKEND
    = new ThreadPoolExecutor(1, 1, 1, TimeUnit.MINUTES, new PriorityBlockingQueue<>());

public static <V> CompletableFuture<V> runAsync(DriverTask<V> dt) {
    CompletableFuture<V> result = new CompletableFuture<>();
    class Job implements Runnable, Comparable<Job>,
                         CompletableFuture.AsynchronousCompletionTask {
        public void run() {
            try {
                if(!result.isDone()) result.complete(dt.call(currentDriver()));
            }
            catch(Throwable t) { result.completeExceptionally(t); }
        }
        private TaskPriority priority() { return dt.getPriority(); }
        public int compareTo(Job o) { return priority().compareTo(o.priority()); }
    }
    BACKEND.execute(new Job());
    return result;
}

Обратите внимание, что реализация CompletableFuture.AsynchronousCompletionTask не обязательна; это просто соглашение для пометки тех Runnable реализаций, целью которых является завершение CompletableFuture.

Еще одно преимущество реализации logi c состоит в том, что на этом первом этапе не нужно переносить исключения в CompletionException. Поэтому, когда вызывающий абонент связывает exceptionally, он увидит исходное развернутое исключение. Кроме того, вызывающий join получит CompletionException, отражающий местоположение кода вызова join с исходным исключением в качестве причины, с гораздо более полезной информацией.

Цель if(!result.isDone()) перед фактической попыткой завершения пропустить ее, если CompletableFuture был отменен (или иным образом завершен) во время ожидания в очереди. Как только попытка завершения была начата, отмена не прервет ее. Это общее поведение CompletableFuture.

...