Comment attendre que toutes les tâches d’un ThreadPoolExecutor se terminent sans fermer le programme d’exécution?

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

Donc, je cherche un moyen d’attendre que ThreadPoolExecutor ait vidé sa queue et terminé toutes ses tâches sans empêcher les nouvelles tâches d’être ajoutées avant ce point.

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

Merci

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

Si vous souhaitez savoir quand une tâche donnée se termine ou s’il vous faut un certain nombre de tâches, vous pouvez utiliser ExecutorService.submit(Runnable) . Invoquer cette méthode renvoie un object Future qui peut être placé dans une Collection que votre thread principal effectuera ensuite en appelant Future.get() pour chacun. Cela empêchera l’exécution de votre thread principal jusqu’à ce que ExecutorService ait traité toutes les tâches Runnable .

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

My Scenario est un robot d’indexation Web qui récupère des informations sur un site Web, puis les traite. Un ThreadPoolExecutor est utilisé pour accélérer le processus car de nombreuses 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 pour le déterminer. Ce n’est pas très élégant mais ça marche 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); 

Vous cherchez peut-être un service CompletionService pour gérer des lots de tâches, voir aussi cette réponse .

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

Je pense que vous pourriez avoir besoin de clarifier votre question car il y a une condition infinie implicite … à un moment donné, vous devez décider de fermer votre exécuteur, et à ce moment-là, il n’acceptera plus de tâches. Votre question semble impliquer que vous voulez attendre de savoir 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 (pour quelque raison que ce soit), en effectuant toutes les tâches actuellement soumises et en ne rejetant pas les nouvelles tâches sur le nouveau TPE. Cela pourrait répondre à votre question. @ Thilo pourrait aussi.

En supposant que vous avez défini quelque part un TPE visible utilisé comme tel:

 AtomicReference publiclyAvailableTPE = ...; 

Vous pouvez ensuite écrire la routine d’échange TPE en tant que telle. Il pourrait également ê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 équipe émetsortingce devra faire face à des tâches rejetées à un moment donné et vous pourriez 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 devrait savoir quoi faire avec un null .

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

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

  1. Parcourez toutes les tâches Future de submit sur ExecutorService et vérifiez le statut avec un appel bloquant get() sur un object Future , comme suggéré par Tim Bender

  2. Utilisez l’un des

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

invokeAll() sur le service exécuteur atteint également le même objective que CountDownLatch

Question SE associée:

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

Vous pourriez appeler le 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