web-dev-qa-db-fra.com

Comment attendre que toutes les tâches d'un ThreadPoolExecutor se terminent sans arrêter l'Executor?

Je ne peux pas utiliser shutdown() et awaitTermination() car il est possible que de nouvelles tâches soient ajoutées à ThreadPoolExecutor en attente.

Je cherche donc un moyen d'attendre que ThreadPoolExecutor ait vidé sa file d'attente et terminé toutes ses tâches sans empêcher l'ajout de nouvelles tâches avant ce point.

Si cela fait une différence, c'est pour Android.

Merci

Mise à jour : Plusieurs semaines plus tard, après avoir revisité cela, j'ai découvert qu'un CountDownLatch modifié fonctionnait mieux pour moi dans ce cas. Je vais garder la réponse marquée parce qu'elle s'applique davantage à ce que j'ai demandé.

58
cottonBallPaws

Si vous souhaitez savoir quand une tâche est terminée ou un certain lot de tâches, vous pouvez utiliser ExecutorService.submit(Runnable). L'appel de cette méthode retourne un objet Future qui peut être placé dans une Collection que votre thread principal itérera ensuite en appelant Future.get() pour chacun. Cela entraînera votre thread principal à arrêter l'exécution jusqu'à ce que ExecutorService ait traité toutes les tâches Runnable.

Collection<Future<?>> futures = new LinkedList<Future<?>>();
futures.add(executorService.submit(myRunnable));
for (Future<?> future:futures) {
    future.get();
}
67
Tim Bender

Mon scénario est un robot Web permettant de récupérer des informations sur un site Web, puis de les traiter. Un ThreadPoolExecutor est utilisé pour accélérer le processus car plusieurs pages peuvent être chargées dans le temps. Ainsi, de nouvelles tâches seront créées dans la tâche existante car le robot suivra les hyperliens de chaque page. Le problème est le même: le thread principal ne sait pas quand toutes les tâches sont terminées et il peut commencer à traiter le résultat. J'utilise un moyen simple de déterminer cela. Ce n'est pas très élégant mais cela fonctionne dans mon cas:

while (executor.getTaskCount()!=executor.getCompletedTaskCount()){
    System.err.println("count="+executor.getTaskCount()+","+executor.getCompletedTaskCount());
    Thread.sleep(5000);
}
executor.shutdown();
executor.awaitTermination(60, TimeUnit.SECONDS);
7
googol4u

Peut-être recherchez-vous un CompletionService pour gérer des lots de tâches, voir aussi cette réponse .

5
Thilo

(Ceci est une tentative de reproduire la réponse supprimée précédemment de Thilo avec mes propres ajustements.) 

Je pense que vous devrez peut-être clarifier votre question car il y a une condition infinie implicite ... à un moment donné, vous devez décider d'arrêter votre exécuteur, et à ce stade, il n'acceptera plus de tâches. Votre question semble impliquer que vous souhaitiez attendre que vous sachiez qu'aucune autre tâche ne sera soumise, que vous ne pouvez connaître que dans votre propre code d'application.

La réponse suivante vous permettra de passer en douceur à un nouveau TPE (quelle que soit la raison) en remplissant toutes les tâches actuellement soumises et en ne rejetant pas de nouvelles tâches dans le nouveau TPE. Cela pourrait répondre à votre question. @ Thilo pourrait aussi. 

En supposant que vous ayez défini quelque part un TPE visible utilisé en tant que tel:

AtomicReference<ThreadPoolExecutor> publiclyAvailableTPE = ...;

Vous pouvez ensuite écrire la routine d’échange TPE en tant que telle. Cela pourrait aussi être écrit en utilisant une méthode synchronisée, mais je pense que c'est plus simple: 

void rotateTPE()
{
   ThreadPoolExecutor newTPE = createNewTPE();
   // atomic swap with publicly-visible TPE
   ThreadPoolExecutor oldTPE = publiclyAvailableTPE.getAndSet(newTPE);
   oldTPE.shutdown();

   // and if you want this method to block awaiting completion of old tasks in  
   // the previously visible TPE
   oldTPE.awaitTermination();
} 

Sinon, si vous ne voulez vraiment pas tuer le pool de threads, votre côté émetteur devra faire face aux tâches rejetées à un moment donné, et vous pouvez utiliser null pour le nouveau TPE:

void killTPE()
{
   ThreadPoolExecutor oldTPE = publiclyAvailableTPE.getAndSet(null);
   oldTPE.shutdown();

   // and if you want this method to block awaiting completion of old tasks in  
   // the previously visible TPE
   oldTPE.awaitTermination();
} 

Ce qui pourrait causer des problèmes en amont, l'appelant aurait besoin de savoir quoi faire avec un null

Vous pouvez également remplacer un TPE factice qui rejette simplement chaque nouvelle exécution, mais cela équivaut à ce qui se produit si vous appelez shutdown() sur le TPE.

3
andersoj

Si vous ne souhaitez pas utiliser shutdown, suivez les approches ci-dessous:

  1. Parcourez toutes les tâches Future de submit on ExecutorService et vérifiez l'état avec l'appel bloquant get() sur l'objet Future comme suggéré par Tim Bender

  2. Utilisez l'un des 

    1. Utilisation de invokeAll on ExecutorService 
    2. Utiliser CountDownLatch
    3. Utilisation de ForkJoinPool ou newWorkStealingPool of Executors (depuis Java 8)

invokeAll() au service de l'exécuteur remplit également le même objectif de CountDownLatch

Question SE connexe:

Comment attendre qu'un certain nombre de threads se terminent?

1
Ravindra babu

Vous pouvez appeler la classe waitTillDone () sur la classe Runner:

Runner runner = Runner.runner(10);

runner.runIn(2, SECONDS, runnable);
runner.run(runnable); // each of this runnables could submit more tasks

runner.waitTillDone(); // blocks until all tasks are finished (or failed)

// and now reuse it

runner.runIn(500, MILLISECONDS, callable);

runner.waitTillDone();
runner.shutdown();

Pour l'utiliser, ajoutez cette dépendance gradle/maven à votre projet: 'com.github.matejtymes:javafixes:1.0'

Pour plus de détails, regardez ici: https://github.com/MatejTymes/JavaFixes ou ici: http://matejtymes.blogspot.com/2016/04/executor-that-notifies-you-when- task.html

0
Matej Tymes

Essayez d'utiliser la taille de la file d'attente et les tâches actives comptent comme indiqué ci-dessous

 while (executor.getThreadPoolExecutor().getActiveCount() != 0 || !executor.getThreadPoolExecutor().getQueue().isEmpty()){
                     try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
            }
        }
0
Tuğrul Karakaya