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é.
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();
}
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);
Peut-être recherchez-vous un CompletionService pour gérer des lots de tâches, voir aussi cette réponse .
(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.
Si vous ne souhaitez pas utiliser shutdown
, suivez les approches ci-dessous:
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
Utilisez l'un des
ExecutorService
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?
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
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) {
}
}