Как использовать CompletableFuture в java 8, чтобы запустить асинхронную задачу и позволить основному потоку завершиться и выйти

У меня есть следующий код (более или менее):

ExecutorService executor = Executors.newFixedThreadPool(10);
CompletableFuture
    .supplyAsync(()->{
        return longRunningMethodThatReturnsBoolean();
    }, executor)
    .thenAcceptAsync(taskResult -> {
        logResult();
        executor.shutdown(); 
    }, executor);

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

Я новичок в этом, я что-то пропустил? Это вообще возможно?

Любая помощь будет оценена!!!


person David Caballero    schedule 02.02.2018    source источник
comment
Что заставляет вас думать, что основной поток остается в живых? Предоставьте минимально воспроизводимый пример.   -  person shmosel    schedule 02.02.2018
comment
Из выхода в заголовке вопроса кажется, что вы хотите, чтобы код в вашей цепочке CompletableFuture продолжал выполняться, пока процесс java завершается? Где тогда будет выполняться CompletableFuture?   -  person Didier L    schedule 02.02.2018


Ответы (2)


На самом деле, если ваш основной поток не ожидает CompletableFuture .get() или любого другого метода блокировки, он умирает, как только достигает конца метода main.

Вы можете проверить это на следующем примере:

public static void main(String[] args){
    final Thread mainThread = Thread.currentThread();
    ExecutorService executor = Executors.newFixedThreadPool(10);
    CompletableFuture
            .supplyAsync(()-> {
                try {
                    Thread.sleep(1000);
                    //prints false
                    System.out.println("Main thread is alive: " + mainThread.isAlive());
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return true;
            }, executor)
            .thenAcceptAsync(taskResult -> {
                System.out.println("LongRunning is finished");
                executor.shutdown();
            }, executor);
}

Но виртуальная машина Java продолжает выполнять потоки до тех пор, пока один из происходит следующее

  • Был вызван метод выхода класса Runtime, и администратор безопасности разрешил выполнение операции выхода.
  • Все потоки, которые не являются потоками не демона, умерли либо в результате возврата из вызова метода запуска, либо в результате создания исключения, которое распространяется за пределы метода запуска.

Это означает, что даже если основной поток мертв, виртуальная машина продолжает работать, поскольку все потоки, созданные Executors.newFixedThreadPool(10), являются не демонами. Вы можете прочитать об этом в документация метода defaultThreadFactory() в классе Executors:

Каждый новый поток создается как поток, не являющийся демоном, с приоритетом, равным наименьшему из значений Thread.NORM_PRIORITY, и максимальным приоритетом, разрешенным в группе потоков.

person Kirill Simonov    schedule 02.02.2018

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

http://hg.openjdk.java.net/jdk7/jdk7/hotspot/file/9b0ca45cd756/src/share/vm/runtime/thread.cpp#l3629

person John H    schedule 02.02.2018
comment
Можете ли вы объяснить, что вы подразумеваете под ожиданием? Я не вижу, как предоставленная ссылка показывает, что основной поток все еще работает после выхода из основного метода. - person tsolakp; 02.02.2018