web-dev-qa-db-fra.com

Quelle est la méthode recommandée pour attendre la fin des futurs threads Completable

J'utilise CompletableFuture comme indiqué ci-dessous dans le code. Mais concernant la façon dont je devrais attendre que tous les runnables se terminent, j'ai trouvé deux façons et je ne connais pas la différence entre elles et laquelle est la meilleure pratique? Ils sont les suivants:

Code:

this.growSeedFutureList = CompletableFuture.runAsync(new GrowSeedSERun(this.saliencyMat, this.seedXY, this.seedVal), this.growSeedExecutor);
this.growSeedFutureList = CompletableFuture.runAsync(new GrowSeedNWRun(this.saliencyMat, this.seedXY, this.seedVal), this.growSeedExecutor);
this.growSeedFutureList = CompletableFuture.runAsync(new GrowSeedNERun(this.saliencyMat, this.seedXY, this.seedVal), this.growSeedExecutor);
this.growSeedFutureList = CompletableFuture.runAsync(new GrowSeedSWRun(this.saliencyMat, this.seedXY, this.seedVal), this.growSeedExecutor);

Première approche pour attendre la fin de tous les runnables:

this.growSeedExecutor.shutdown();
this.growSeedExecutor.awaitTermination(1, TimeUnit.DAYS);

Deuxième approche pour attendre la fin de tous les runnables:

CompletableFuture.allOf(this.growSeedFutureList).join();

Veuillez me faire savoir lequel est recommandé.

20
user2121

Les deux façons sont équivalentes uniquement lorsque l'exécuteur (growSeedExecutor) est utilisé uniquement pour la tâche donnée. La première façon peut conduire à ce qui suit: Une autre tâche nécessite une parallélisation et un nouvel exécuteur est créé pour chaque tâche. Certains développeurs voient trop d'exécuteurs créés et décident d'utiliser un seul exécuteur commun, mais n'ont pas réussi à supprimer tous les arrêts d'exécuteur ...

Ainsi, la deuxième méthode (join ()) est plus fiable, car elle est moins complexe. Mais chaque nouvel avenir doit être ajouté à growSeedFutureList, et non affecté à.

8

Si vous voulez vraiment attendre tous les futurs, vous pouvez simplement appeler join() sur chacun d'eux:

growSeedFutureList.forEach(CompletableFuture::join);

La principale différence par rapport à l'utilisation de allOf() est que cela lèvera une exception dès qu'il atteindra un futur terminé avec une exception, tandis que la version allOf().join() ne lancera une exception qu'après tous les futurs ont terminé (exceptionnellement ou non).

Une autre petite différence est que cela ne crée pas l'étape intermédiaire allOf. Une telle étape reste utile si vous voulez faire quelque chose de manière asynchrone une fois tous les futurs terminés, au lieu d'attendre qu'ils se terminent tous.

La solution avec l'exécuteur de l'autre côté présente plusieurs inconvénients:

  • il empêche de réutiliser l'exécuteur car il nécessite son arrêt;
  • il vous oblige à utiliser cet exécuteur pour toutes les opérations - il ne fonctionnera pas avec les CompletableFutures qui sont gérés d'une autre manière;
  • il ne montre pas clairement votre intention, qui est d'attendre que tous les futurs se terminent;
  • il est plus complexe à mettre en œuvre;
  • il ne gère pas l'achèvement exceptionnel - aucune exception ne sera levée par awaitTermination() si l'une des tâches a échoué.
10
Didier L