отложите выполнение приложения ApplyAsync - PullRequest
0 голосов
/ 07 июня 2018

У меня следующий сценарий.

CompletableFuture<T> result = CompletableFuture.supplyAsync(task, executor);
result.thenRun(() -> {
        ...
    });
// ....
// after some more code, based on some condition I attach the thenApply() to result.

if ( x == 1) {
    result.thenApplyAsync(t -> {

        return null;
    });
}

Вопрос в том, что если поток CompletableFuture завершит выполнение до того, как основной поток достигнет thenApplyAsync?результат CompletableFuture должен присоединиться к thenApply.т.е. должен ли быть объявлен обратный вызов во время определения самого CompletableFuture.supplyAsync()? 1009 *

И каков порядок выполнения?thenRun() всегда выполняется в конце (после thenApply())?

Есть ли какой-либо недостаток для использования этой стратегии?

1 Ответ

0 голосов
/ 07 июня 2018

Вы, похоже, упускаете важный момент.Когда вы связываете в цепочку зависимую функцию, вы не изменяете будущее, в котором вы вызываете метод сцепления.

Вместо этого каждый из этих методов возвращает завершение new Этап, представляющий зависимое действие.

Так как вы присоединяете два зависимых действия к result, которые представляют task, переданное supplyAsync, между этими двумя действиями нет никакой связи.Они могут выполняться в произвольном порядке и даже одновременно в разных потоках.

Поскольку вы нигде не сохраняете будущее, возвращаемое thenApplyAsync, результат его оценки в любом случае будет потерян.Предполагая, что ваша функция возвращает результат того же типа, что и T, вы можете использовать

if(x == 1) {
    result = result.thenApplyAsync(t -> {

        return null;
    });
}

, чтобы заменить потенциально завершенное будущее новым будущим, которое завершается только тогда, когда результат указанной функции имеетбыли оценены.Runnable, зарегистрированный в исходном будущем через thenRun, все еще не зависит от этого нового будущего.Обратите внимание, что thenApplyAsync без исполнителя всегда будет использовать исполнителя по умолчанию, независимо от того, какой исполнитель использовался для завершения другого будущего.

Если вы хотите убедиться, что Runnable был успешно выполнен перед любым другимstage, вы можете использовать

CompletableFuture<T> result = CompletableFuture.supplyAsync(task, executor);
CompletableFuture<Void> thenRun = result.thenRun(() -> {
    //...
});
result = result.thenCombine(thenRun, (t,v) -> t);

Альтернативой будет

result = result.whenComplete((value, throwable) -> {
    //...
});

, но здесь код всегда будет выполняться даже в исключительном случае (который включает отмену).Вам нужно проверить, является ли throwable значением null, если вы хотите выполнить код только в успешном случае.


Если вы хотите убедиться, что запускаемый файл запускается после обоих действий,Простейшей стратегией было бы связать ее после оператора if, когда определен финальный этап завершения:

if(x == 1) {
    result = result.thenApplyAsync(t -> {

        return null;
    });
}
result.thenRun(() -> {
    //...
});

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

CompletableFuture<T> result = CompletableFuture.supplyAsync(task, executor);

//...

CompletableFuture<T> finalStage = new CompletableFuture<>();
finalStage.thenRun(() -> {
    //...
});

// ...

if(x == 1) {
    result = result.thenApplyAsync(t -> {

        return null;
    });
}
result.whenComplete((v,t) -> {
    if(t != null) finalStage.completeExceptionally(t); else finalStage.complete(v);
});

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


В качестве последнего замечания, методы без …Async, такие как thenRunобеспечить наименьший контроль над потоком оценки.Они могут выполняться в любом потоке, завершившем будущее, например, в одном из потоков executor в вашем примере, но также непосредственно в потоке, вызывающем thenRun, и даже менее интуитивно понятным, в вашем исходном примере исполняемый файл может быть выполненво время несвязанного thenApplyAsync вызова.

...