Java 8 CompletableFuture de calcul paresseux de contrôle


J'ai une question sur CompletableFuture et son utilisation possible pour les calculs paresseux.

Il semble que ce soit un excellent substitut à RunnableFuture pour cette tâche car il est possible de créer facilement des chaînes de tâches et d'avoir le contrôle total de chaque maillon de la chaîne. Pourtant, j'ai trouvé qu'il est très difficile de contrôler quand exactement le calcul a-t-il lieu.

Si je crée juste un CompletableFuture avec la méthode supplyAssync ou quelque chose comme ça, c'est OK. Il attend patiemment que j'appelle get ou join méthode de calcul. Mais si j'essaie de faire une chaîne réelle avec whenCompose, handle ou toute autre méthode, l'évaluation commence immédiatement, ce qui est très frustrant.

Bien sûr, je peux toujours placer une tâche de blocage au début de la chaîne et libérer le bloc lorsque je suis prêt à commencer le calcul, mais cela semble une solution un peu moche. Est-ce que quelqu'un sait comment contrôler quand CompletableFuture s'exécute réellement.

Author: Darksnake, 2016-07-17

3 answers

Il y a une différence conceptuelle entre RunnableFuture et CompletableFuture qui vous manque ici.

  • RunnableFuture les implémentations prennent une tâche en entrée et la conservent. Il exécute la tâche lorsque vous appelez la run méthode.
  • A CompletableFuture ne tient pas sur une tâche. Il connait le résultat d'une tâche. Il y a trois états: complète, incomplète, et terminé exceptionnellement (échec).

CompletableFuture.supplyAsync est une méthode d'usine qui vous donne un incomplet CompletableFuture. Il planifie également une tâche qui, une fois terminée, transmettra son résultat à la méthode CompletableFuture's complete. En d'autres termes, l'avenir que supplyAsync vous tend ne sait rien de la tâche et ne peut pas contrôler quand la tâche s'exécute.

Pour utiliser un CompletableFuture dans la façon dont vous décrivez, vous devez créer une sous-classe:

public class RunnableCompletableFuture<T> extends CompletableFuture<T> implements RunnableFuture<T> {
    private final Callable<T> task;

    public RunnableCompletableFuture(Callable<T> task) {
        this.task = task;
    }

    @Override
    public void run() {
        try {
            complete(task.call());
        } catch (Exception e) {
            completeExceptionally(e);
        }
    }
}
 3
Author: Sam, 2016-07-17 12:45:17

Une façon simple de traiter votre problème consiste à envelopper votre CompletableFuture dans quelque chose de nature paresseuse. Vous pouvez utiliser un fournisseur ou même un flux Java 8.

 0
Author: Pedro David, 2016-07-17 12:59:34

CompletableFuture est un push-design, c'est-à-dire que les résultats sont poussés vers les tâches dépendantes dès qu'ils sont disponibles. Cela signifie également que les chaînes secondaires qui ne sont pas consommées en elles-mêmes sont toujours exécutées, ce qui peut avoir des effets secondaires.

Ce que vous voulez, c'est un pull-design où les ancêtres ne seraient extraits que lorsque leurs données sont consommées. Ce serait une conception fondamentalement différente car les effets secondaires des arbres non consommés ne se produiraient jamais.

Bien sûr avec assez les contorsions CF pourraient être faites pour faire ce que vous voulez, mais vous devriez plutôt regarder dans le cadre fork-join qui vous permet de n'exécuter que les calculs dont vous dépendez au lieu de réduire les résultats.

 0
Author: the8472, 2016-07-17 13:16:30