Зачем использовать завершаемое будущее, если задачи зависят - PullRequest
0 голосов
/ 08 октября 2019

Я очень эффективно использую ExecutorServices в своем приложении, которое имеет дело с обработкой заказа, теперь жизненный цикл заказа состоит из нескольких этапов, которые необходимо выполнить последовательно. тогда как 2 заказа должны быть обработаны независимо друг от друга.

Код Sudo выглядит следующим образом:

ExecutorService service = Executors.newFixedThreadPool(100);
    List<Future<Boolean>> orderStatus = new ArrayList<>(); 
    for (int i = 0; i < 100 ; i++) {
        Future<Boolean> status = service.submit(() -> {
            ProcessOrder();
        });
        orderStatus.add(status);
    }

public Boolean ProcessOrder(){
   Order order = PollOrder();
   order =  EnrichOrder(order);
   order =  Payment(order);
   order =  confirmOrder(order);
   return true;
}

с другой стороны, если я использую CompleteableFuture, единственным преимуществом, которое я вижу, является использование общего пула forkjoin, где код выглядит простыми много читабельных, но поскольку задачи взаимозависимы при обработке одного и того же порядка, каково практическое преимущество использования CompleteableFuture, когда get () блокируется в любом случае.

for (int i = 0; i < 100 ; i++) {
    CompletableFuture<Order> orderStatus= CompletableFuture.supplyAsync(()->pollOrder())
            .thenApply(order -> enrichOrder(order))
            .thenApply(order -> payment(order))
            .thenApply(order -> confirmOrder(order)); 
}

1 Ответ

1 голос
/ 08 октября 2019

Я думаю, что одним из преимуществ является лучшее использование пулов потоков. Пример кода ExecutorService использует один и тот же пул для каждой операции. Эти операции могут быть интенсивными ввода-вывода или вычисления. Выполнение этих операций в разных пулах лучше использует системные ресурсы. (*) Очень легко запускать задачи в разных пулах с асинхронными методами CompletableFuture.

CompletableFuture.supplyAsync(()->comp1()) // start in common-pool
            .thenApplyAsync(order -> io1(order),ioPool) // lets say this is IO operation 
            .thenApplyAsync(order -> comp2(order)) // switch back to common-pool
            .thenApplyAsync(order -> io2(order),ioPool); // another io

В этом примере, когда задача comp1 завершается, задача io1будет выполняться в пуле потоков ввода-вывода, и потоки общего пула могут выполнять другие задачи в течение этого времени. В конце задачи io1 задача comp2 будет передана в общий пул.

Вы можете достичь того же, не используя CompletableFuture, но код будет более сложным. (например, передача задачи comp2 методу io1 в качестве параметра и передача его из метода io1 в общий пул в конце.)

Кроме того, при написании асинхронного кода, я думаю, что конвейер completetableFuture должен быть завершен с другим асинхронным вызовом вместоМетод get.

(*) Скажем, это код, работающий на 8-ядерном компьютере, отправка задачи 100 вычислений в этот пул из 100 потоков не будет работать лучше, чем при запуске их 8 одновременно.

...