Поведение ForkJoinPool в CompletableFuture.supplyAsync () - PullRequest
0 голосов
/ 04 января 2019

Я сравниваю поведение CompletableFuture.supplyAsync () в двух случаях, когда я устанавливаю пользовательскую службу ExecutorService или я хочу, чтобы мой поставщик выполнялся исполнителем по умолчанию(если не указано), которое ForkJoinPool.commonPool ()

Давайте посмотрим на разницу:

public class MainApplication {
  public static void main(final String[] args) throws ExecutionException, InterruptedException {

    Supplier<String> action1 = () -> {
        try {
            Thread.sleep(3000);
        }finally {
            return "Done";
        }
    };
    Function<String, String> action2 = (input) -> {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            return input + "!!";
        }
    };

    final ExecutorService executorService = Executors.newFixedThreadPool(4);

    CompletableFuture.supplyAsync(action1, executorService)
                     .thenApply  (action2)
                     .thenAccept (res -> System.out.println(res));

    System.out.println("This is the end of the execution");

  }
}

В этом случае яПередача executorService в мой supplyAsync (), и он печатает:

Это конец выполнения

Готово !!

Итак «Готово» печатается после окончания основного исполнения.

НО, если я использую вместо:

CompletableFuture.supplyAsync(action1)

, поэтому я не передаю свой обычайexecutorService и класс CompletableFuture используют под капотом ForkJoinPool.commonPool () , тогда «Done» вообще не печатается:

Это конец выполнения

Процесс завершен с кодом выхода 0

Почему?

Ответы [ 2 ]

0 голосов
/ 04 января 2019

В обоих случаях, когда вы делаете

CompletableFuture.supplyAsync(action1, executorService)
                     .thenApply  (action2)
                     .thenAccept (res -> System.out.println(res));

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

ForkJoinPool.commonPool()

и обычная служба исполнителя:

final ExecutorService executorService = Executors.newFixedThreadPool(4);

.. реагируют на попытку вызова System.эквивалент выхода (...).

Вот что doc говорит о общем пуле объединения вилок, вам следует обратить на это внимание:

Однако этот пули любая текущая обработка автоматически прекращается по программе System.exit (int) .Любая программа, которая полагается на асинхронную обработку задачи для завершения до ее завершения, должна вызывать commonPool (). AwaitQuiescence перед выходом.

То есть ссылка на Документы ExecutorService , на которые вы можете обратить вниманиеto:

Метод shutdown () позволит выполнить ранее отправленные задачи перед завершением

Я думаю, это может быть различие, о котором вы спрашиваете.

0 голосов
/ 04 января 2019

ForkJoinPool использует потоки демона, которые не препятствуют выходу JVM.С другой стороны, потоки в ExecutorService, созданные Executors, являются потоками, не являющимися демонами, поэтому он не позволяет JVM завершаться до тех пор, пока вы явно не закроете пул потоков.

Также обратите внимание, что в вашем примере вам необходимо завершить пул в конце, чтобы завершить работу JVM.

executorService.shutdown();

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

Thread.sleep(4000);
...