ExecutorService.submit(Task) vs CompletableFuture.supplyAsync(Task, Executor)

Чтобы запускать некоторые вещи параллельно или асинхронно, я могу использовать либо ExecutorService: <T> Future<T> submit(Runnable task, T result);, либо CompletableFuture Api: static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor); (Предположим, что я использую в обоих случаях тот же Исполнитель)

Помимо возвращаемого типа Future vs. CompletableFuture есть ли какие-либо примечательные отличия. Или Когда использовать что?

И каковы различия, если я использую CompletableFutureApi со стандартным Executor (метод без исполнителя)?

Ответ 1

Помимо возвращаемого типа Future vs. CompletableFuture есть ли какие-либо примечательные отличия. Или Когда использовать что?

Это довольно просто. Вы используете Future, когда хотите, чтобы исполняемый поток ожидал ответа на вычисление async. Примером этого является параллельное слияние/сортировка. Сортировка слева асинхронно, сортировка справа синхронно, ждать слева для завершения (future.get()), слить результаты.

Вы используете CompleteableFuture, когда хотите выполнить какое-либо действие с результатом после завершения, асинхронно из исполняемого потока. Например: я хочу сделать некоторые вычисления асинхронно, и когда я вычислил, напишите результаты в какую-то систему. Запрашивающий поток, возможно, не нужно ждать результата.

Вы можете воспроизвести приведенный выше пример в одном исполняемом файле Future, но CompletableFuture предлагает более свободный интерфейс с улучшенной обработкой ошибок.

Это действительно зависит от того, что вы хотите сделать.

И каковы различия, если я использую CompletableFutureApi со стандартным Executor (метод без исполнителя)?

Он будет делегировать ForkJoin.commonPool(), который является размером по умолчанию для количества процессоров в вашей системе. Если вы делаете что-то интенсивное вложение (чтение и запись в файловую систему), вы должны определить пул потоков по-разному.

Если процессор интенсивный, использование commonPool имеет наибольший смысл.

Ответ 2

CompletableFuture имеет богатые функции, такие как объединение нескольких фьючерсов, объединение фьючерсов, выполнение некоторых действий после будущего исполнения (как синхронно, так и асинхронно) и т.д.

Однако, CompletableFuture не отличается от Future в плане производительности. Даже при объединении нескольких экземпляров CompletableFuture (с использованием .thenCombine и .join в конце) ни один из них не выполняется, если мы не вызываем метод .get, и в течение этого времени вызывающий поток блокируется. Я чувствую себя с точки зрения производительности, это не лучше, чем будущее.

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