web-dev-qa-db-fra.com

Que ce soit pour utiliser invokeAll ou submit - Java Service Executor

J'ai un scénario où je dois exécuter 5 thread asynchrone pour le même appelable. Pour autant que je comprends, il y a deux options:

1) en utilisant soumettre (appelable)

ExecutorService executorService = Executors.newFixedThreadPool(5);
List<Future<String>> futures = new ArrayList<>();
for(Callable callableItem: myCallableList){
    futures.add(executorService.submit(callableItem));
}

2) en utilisant invokeAll (Collections de Callable)

ExecutorService executorService = Executors.newFixedThreadPool(5);
List<Future<String>> futures = executorService.invokeAll(myCallableList));
  1. Quelle devrait être la voie préférée?
  2. Y a-t-il un inconvénient ou un impact sur les performances dans l'un d'eux par rapport à l'autre?
17
Ankit

Option 1 : vous soumettez les tâches à ExecutorService et vous n'attendez pas la fin de toutes les tâches qui ont été soumises à ExecutorService

Option 2 : Vous attendez la fin de toutes les tâches qui ont été soumises à ExecutorService.

Quelle devrait être la voie préférée?

Selon les exigences de l'application, l'un ou l'autre est préférable.

  1. Si vous ne voulez pas attendre la soumission de la tâche () à ExecutorService, préférez Option 1.
  2. Si vous devez attendre la fin de toutes les tâches qui ont été soumises à ExecutorService, préférez Option 2.

Y a-t-il un inconvénient ou un impact sur les performances dans l'un d'eux par rapport à l'autre?

Si votre application requiert l'option 2, vous devez attendre la fin de toutes les tâches soumises à ExecutorService contrairement à l'option 1. Les performances ne sont pas des critères de comparaison car les deux sont conçues pour deux objectifs différents.

Et encore une chose importante: quelle que soit l'option que vous préférez, FutureTask avale les exceptions pendant l'exécution de la tâche. Tu dois être prudent. Jetez un œil à cette question SE: Gestion des exceptions pour ThreadPoolExecutor

Avec Java 8, vous avez encore une option: ExecutorCompletionService

A CompletionService qui utilise un exécuteur fourni pour exécuter les tâches. Cette classe dispose que les tâches soumises sont, une fois terminées, placées dans une file d'attente accessible à l'aide de take. La classe est suffisamment légère pour convenir à une utilisation transitoire lors du traitement de groupes de tâches.

Jetez un oeil à la question SE connexe: ExecutorCompletionService? Pourquoi en avez-vous besoin si nous avons invokeAll?

16
Ravindra babu

ÉDITER:

Il y a en fait une différence entre eux. Pour une raison quelconque, invokeAll() appellera get() pour chaque future produit. Ainsi, il attendra que les tâches se terminent et c'est pourquoi il peut lancer InterruptedException (tandis que submit() ne lance rien).

C'est le Javadoc pour la méthode invokeAll():

Exécute les tâches données, renvoyant une liste de Futures contenant leur statut et leurs résultats quand tout est terminé.

Donc, les deux stratégies font essentiellement la même chose, mais si vous appelez invokeAll() vous serez bloqué jusqu'à ce que toutes les tâches soient terminées.


Réponse originale (incomplète):

La méthode invokeAll() est là exactement pour des situations comme celles-ci. Vous devez certainement l'utiliser.

Vous n'avez pas vraiment besoin d'instancier ce List, cependant:

ExecutorService executorService = Executors.newFixedThreadPool(5);
List<Future<String>> futures = executorService.invokeAll(myCallableList));

Cela devrait suffire et cela semble beaucoup plus propre que la première alternative.

3
Fred Porciúncula

Supposons que vous ayez une tâche dont le résultat dépend du nombre de tâches exécutables indépendamment. Mais pour terminer la tâche initiale, vous n'avez qu'un temps limité. Comme c'est un appel API.

Ainsi, par exemple, vous disposez de 100 ms pour terminer la tâche de niveau supérieur et il existe également 10 tâches dépendantes. Pour cela, si vous utilisez une soumission ici, à quoi ressemblera le code.

List<Callable> tasks = []// assume contains sub tasks
List<Future> futures = [] 
for(Callable task: tasks) {
   futures.add(service.submit(task));
}

for(Future futute: futures) {
    future.get(100, TimeUnit.MILLISECONDS);
}

Donc, si chacune des sous-tâches prenait 50 ms exactement pour terminer le morceau de code ci-dessus, cela prendrait 50 ms. Mais si chacune des sous-tâches prenait 1000 ms pour terminer ce qui précède prendrait 100 * 10 = 1000 ms ou 1s. Cela rend difficile de calculer le temps total à moins de 100 ms pour toutes les sous-tâches.

la méthode invokeAll nous aide dans un tel scénario

List<Futures> futures = service.invokeall(tasks, 100, TimeUnit.MILLISECONDS)
for(Future future: futures) {
   if(!future.isCancelled()) {
       results.add(future.get());
   }
}

De cette façon, le temps maximum que cela prendrait est de 100 ms, même si l'individu des sous-tâches prenait plus que cela.

1
parampreet bawa