J'ai une méthode qui renvoie un List
des futurs
List<Future<O>> futures = getFutures();
Maintenant, je veux attendre que tous les futurs soient traités avec succès ou que l'une des tâches dont la sortie est retournée par un futur lève une exception. Même si une tâche lève une exception, il ne sert à rien d'attendre les autres futurs.
Une approche simple serait de
wait() {
For(Future f : futures) {
try {
f.get();
} catch(Exception e) {
//TODO catch specific exception
// this future threw exception , means somone could not do its task
return;
}
}
}
Mais le problème ici est que si, par exemple, le 4ème futur lève une exception, alors j'attendrai inutilement que les 3 premiers futurs soient disponibles.
Comment résoudre ça? Le compte à rebours du verrou vous aidera-t-il d'une manière ou d'une autre? Je ne peux pas utiliser Future isDone
car le document java dit
boolean isDone()
Returns true if this task completed. Completion may be due to normal termination, an exception, or cancellation -- in all of these cases, this method will return true.
java
multithreading
future
user93796
la source
la source
ExecutionService
pour chaque "lot" de tâches, les lui soumettre, puis arrêter immédiatement le service et l'utiliserawaitTermination()
, je suppose.CountDownLatch
si vous enveloppez le corps de tous vos futurs dans untry..finally
pour vous assurer que le loquet est également décrémenté.Réponses:
Vous pouvez utiliser un CompletionService pour recevoir les contrats à terme dès qu'ils sont prêts et si l'un d'eux émet une exception, annulez le traitement. Quelque chose comme ça:
Je pense que vous pouvez encore améliorer pour annuler toutes les tâches en cours d'exécution si l'une d'entre elles génère une erreur.
la source
CompletionService
.Si vous utilisez Java 8, vous pouvez le faire plus facilement avec CompletableFuture et CompletableFuture.allOf , qui n'appliquent le rappel qu'une fois que tous les CompletableFutures fournis sont terminés.
la source
Future
instances, vous ne pouvez pas appliquer cette méthode. Ce n'est pas facile de se convertirFuture
enCompletableFuture
.Utiliser un
CompletableFuture
dans Java 8la source
Vous pouvez utiliser un ExecutorCompletionService . La documentation a même un exemple pour votre cas d'utilisation exact:
La chose importante à noter ici est que ecs.take () obtiendra la première tâche terminée , pas seulement la première soumise. Ainsi, vous devriez les obtenir dans l'ordre de terminer l'exécution (ou de lancer une exception).
la source
Si vous utilisez Java 8 et que vous ne voulez pas manipuler
CompletableFuture
s, j'ai écrit un outil pour récupérer les résultats pour uneList<Future<T>>
utilisation du streaming. La clé est qu'il vous est interdit demap(Future::get)
lancer.Cela nécessite un
AggregateException
qui fonctionne comme C #Ce composant agit exactement comme Task.WaitAll de C # . Je travaille sur une variante qui fait la même chose que
CompletableFuture.allOf
(équivalent àTask.WhenAll
)La raison pour laquelle j'ai fait cela est que j'utilise Spring
ListenableFuture
et que je ne veux pas porter surCompletableFuture
malgré que ce soit un moyen plus standardla source
Dans le cas où vous souhaitez combiner une liste de CompletableFutures, vous pouvez le faire:
Pour plus de détails sur Future & CompletableFuture, liens utiles:
1. Future: https://www.baeldung.com/java-future
2. CompletableFuture: https://www.baeldung.com/java-completablefuture
3. CompletableFuture: https : //www.callicoder.com/java-8-completablefuture-tutorial/
la source
peut-être que cela aiderait (rien ne serait remplacé par un fil brut, ouais!) Je suggère d'exécuter chaque
Future
gars avec un fil séparé (ils vont en parallèle), puis chaque fois qu'une erreur a eu lieu, cela signale simplement le gestionnaire (Handler
classe).Je dois dire que le code ci-dessus serait une erreur (n'a pas été vérifié), mais j'espère pouvoir expliquer la solution. Veuillez essayer.
la source
la source
Le CompletionService prendra vos Callables avec la méthode .submit () et vous pourrez récupérer les futurs calculés avec la méthode .take ().
Une chose que vous ne devez pas oublier est de mettre fin à ExecutorService en appelant la méthode .shutdown (). De plus, vous ne pouvez appeler cette méthode que lorsque vous avez enregistré une référence au service exécuteur, alors assurez-vous d'en conserver une.
Exemple de code - Pour un nombre fixe d'éléments de travail à travailler en parallèle:
Exemple de code - Pour un nombre dynamique d'éléments de travail à travailler en parallèle:
la source
J'ai une classe utilitaire qui contient ceux-ci:
Une fois que vous avez cela, en utilisant une importation statique, vous pouvez simplement attendre tous les futurs comme ceci:
vous pouvez également collecter tous leurs résultats comme ceci:
Je revisite simplement mon ancien message et remarquez que vous avez eu un autre chagrin:
Dans ce cas, la solution simple est de le faire en parallèle:
De cette façon, la première exception, même si elle n'arrêtera pas l'avenir, cassera l'instruction forEach, comme dans l'exemple série, mais comme tous attendent en parallèle, vous n'aurez pas à attendre que les 3 premiers se terminent.
la source
la source