Exécutorcompletionservice? Pourquoi en avoir besoin si nous en avons tous?

si nous utilisons un ExecutorCompletionService nous pouvons soumettre une série de tâches comme Callable s et obtenir le résultat en interaction avec le CompletionService comme un queue .

Mais il y a aussi le invokeAll de ExecutorService qui accepte un Collection de tâches et nous obtenons une liste de Future pour récupérer les résultats.

autant que je puisse dire, il n'y a aucun avantage à utiliser l'un ou l'autre (sauf que nous évitons une boucle for en utilisant un invokeAll que nous aurions à submit les tâches au CompletionService ) et essentiellement ils sont la même idée avec une légère différence.

alors pourquoi y a-t-il deux façons différentes de soumettre une série de tâches? Ai-je raison de dire que les performances sont équivalentes? Est-il un cas que l'un est plus adapté que les autres? Je ne peux pas penser à un.

33
demandé sur Basil Bourque 2012-08-09 00:20:20

4 réponses

en utilisant un ExecutorCompletionService.poll/take , vous recevez le Future s comme ils terminent, dans l'ordre d'achèvement (plus ou moins). En utilisant ExecutorService.invokeAll , vous n'avez pas ce pouvoir; soit vous bloquez jusqu'à ce que tout soit terminé, soit vous spécifiez un délai après lequel les incomplets sont annulés.


static class SleepingCallable implements Callable<String> {

  final String name;
  final long period;

  SleepingCallable(final String name, final long period) {
    this.name = name;
    this.period = period;
  }

  public String call() {
    try {
      Thread.sleep(period);
    } catch (InterruptedException ex) { }
    return name;
  }
}

maintenant, ci-dessous je vais vous montrer comment invokeAll fonctionne:

final ExecutorService pool = Executors.newFixedThreadPool(2);
final List<? extends Callable<String>> callables = Arrays.asList(
    new SleepingCallable("quick", 500),
    new SleepingCallable("slow", 5000));
try {
  for (final Future<String> future : pool.invokeAll(callables)) {
    System.out.println(future.get());
  }
} catch (ExecutionException | InterruptedException ex) { }
pool.shutdown();

produit le résultat suivant:

C:\dev\scrap>java CompletionExample
... after 5 s ...
quick
slow

en utilisant CompletionService , nous voyons une sortie différente:

final ExecutorService pool = Executors.newFixedThreadPool(2);
final CompletionService<String> service = new ExecutorCompletionService<String>(pool);
final List<? extends Callable<String>> callables = Arrays.asList(
    new SleepingCallable("slow", 5000),
    new SleepingCallable("quick", 500));
for (final Callable<String> callable : callables) {
  service.submit(callable);
}
pool.shutdown();
try {
  while (!pool.isTerminated()) {
    final Future<String> future = service.take();
    System.out.println(future.get());
  }
} catch (ExecutionException | InterruptedException ex) { }

produit le résultat suivant:

C:\dev\scrap>java CompletionExample
... after 500 ms ...
quick
... after 5 s ...
slow

notez que les heures sont relatives au démarrage du Programme, pas au message précédent.


vous pouvez trouver le code complet sur les deux ici .

62
répondu oldrinb 2012-08-09 19:19:54

en utilisant un ExecutorCompletionService , vous pouvez être immédiatement informé lorsque chacun de vos travaux est terminé. En comparaison, ExecutorService.invokeAll(...) attend pour tout de vos travaux à compléter avant de retourner la collecte de Future s:

// this waits until _all_ of the jobs complete
List<Future<Object>> futures = threadPool.invokeAll(...);

au lieu de cela, lorsque vous utilisez un ExecutorCompletionService , vous serez en mesure d'obtenir les travaux dès que chacun d'eux complète ce qui vous permet (par exemple) de les envoyer pour le traitement dans un autre pool de thread, log les résultats, etc..

ExecutorService threadPool = Executors.newFixedThreadPool(2);
ExecutorCompletionService<Result> compService
      = new ExecutorCompletionService<Result>(threadPool);
for (MyJob job : jobs) {
    compService.submit(job);
}
// shutdown the pool but the jobs submitted continue to run
threadPool.shutdown();
while (!threadPool.isTerminated()) {
    // the take() blocks until any of the jobs complete
    // this joins with the jobs in the order they _finish_
    Future<Result> future = compService.take();
    // this get() won't block
    Result result = future.get();
    // you can then put the result in some other thread pool or something
    // to immediately start processing it
    someOtherThreadPool.submit(new SomeNewJob(result));
}
17
répondu Gray 2018-06-21 18:06:20

Je n'ai jamais réellement utilisé Executorcompletservice, mais je pense que le cas où cela pourrait être plus utile que" normal " ExecutorService serait quand vous voulez recevoir les Futures des tâches terminées dans l'ordre d'achèvement. Avec invokeAll, vous obtenez juste une liste qui peut contenir un mélange de tâches incomplètes et achevées à tout moment.

2
répondu esaj 2012-08-08 20:27:01

la Comparaison en Considérant seulement l'Ordre des Résultats:

lorsque nous utilisons CompletionService chaque fois qu'une tâche Soumise est terminée, le résultat sera poussé dans la file d'attente (ordre d'achèvement). Puis l'ordre des travaux soumis et les résultats retournés n'est plus le même. Donc, si vous êtes préoccupé par l'ordre quelles tâches ont été exécutées utiliser CompletionService

où comme invokeAll renvoie une liste de Futures représentant les tâches, dans le même l'ordre établi par l'itérateur pour la liste des tâches, chacun de qui a terminé.

1
répondu Dungeon Hunter 2013-01-14 19:49:10