Quelle est la manière la plus simple d'attendre que toutes les tâches ExecutorService
se terminent? Ma tâche est principalement informatique, donc je veux juste exécuter un grand nombre de travaux - un sur chaque cœur. En ce moment, ma configuration ressemble à ceci:
ExecutorService es = Executors.newFixedThreadPool(2);
for (DataTable singleTable : uniquePhrases) {
es.execute(new ComputeDTask(singleTable));
}
try{
es.wait();
}
catch (InterruptedException e){
e.printStackTrace();
}
ComputeDTask
implémente runnable. Cela semble exécuter correctement les tâches, mais le code se bloque wait()
avec IllegalMonitorStateException
. C'est étrange, parce que j'ai joué avec des exemples de jouets et que cela semblait fonctionner.
uniquePhrases
contient plusieurs dizaines de milliers d'éléments. Dois-je utiliser une autre méthode? Je cherche quelque chose d'aussi simple que possible
java
multithreading
threadpool
executorservice
george smiley
la source
la source
es
) quand vous voulez l'attendre - le verrou sera automatiquement libéré en attendantExecutors.newFixedThreadPool(System.getRuntime().availableProcessors());
Réponses:
L'approche la plus simple consiste à utiliser
ExecutorService.invokeAll()
ce qui fait ce que vous voulez dans une doublure. Dans votre langage, vous devrez modifier ou encapsulerComputeDTask
pour implémenterCallable<>
, ce qui peut vous donner un peu plus de flexibilité. Probablement, dans votre application, il existe une implémentation significative deCallable.call()
, mais voici un moyen de l'envelopper si vous ne l'utilisez pasExecutors.callable()
.Comme d'autres l'ont souligné, vous pouvez utiliser la version timeout de
invokeAll()
si nécessaire. Dans cet exemple,answers
va contenir un tas deFuture
s qui renverra des valeurs nulles (voir la définition deExecutors.callable()
. Probablement ce que vous voulez faire est une légère refactorisation afin que vous puissiez obtenir une réponse utile ou une référence au sous-jacentComputeDTask
, mais je peux ne dis rien de ton exemple.Si ce n'est pas clair, notez que
invokeAll()
cela ne reviendra pas tant que toutes les tâches ne seront pas terminées. (c'est-à-dire que tous lesFuture
s de votreanswers
collection seront.isDone()
signalés si demandé.) Cela évite tout arrêt manuel, waitTermination, etc ... et vous permet de le réutiliserExecutorService
proprement pour plusieurs cycles, si vous le souhaitez.Il y a quelques questions connexes sur SO:
Comment attendre la fin de tous les threads
Renvoyer les valeurs des threads Java
invokeAll () ne souhaite pas accepter une collection <Callable <t>>
Dois-je synchroniser?
Aucun de ces éléments n'est strictement pertinent pour votre question, mais ils fournissent un peu de couleur sur la façon dont les gens pensent
Executor
/ExecutorService
devraient être utilisés.la source
Si vous souhaitez attendre la fin de toutes les tâches, utilisez la
shutdown
méthode au lieu dewait
. Ensuite, suivez-le avecawaitTermination
.Vous pouvez également utiliser
Runtime.availableProcessors
pour obtenir le nombre de threads matériels afin d'initialiser correctement votre pool de threads.la source
awaitTermination
nécessite un délai d'expiration comme paramètre. Bien qu'il soit possible de fournir un temps fini et de placer une boucle autour pour attendre que tous les threads soient terminés, je me demandais s'il y avait une solution plus élégante.Si attendre que toutes les tâches soient
ExecutorService
terminées n'est pas précisément votre objectif, mais plutôt attendre qu'un lot spécifique de tâches soit terminé, vous pouvez utiliser unCompletionService
- spécifiquement, unExecutorCompletionService
.L'idée est de créer une
ExecutorCompletionService
enveloppeExecutor
, de soumettre un certain nombre de tâches connues via leCompletionService
, puis de tirer le même nombre de résultats de la file d'attente d'achèvement en utilisanttake()
(quels blocs) oupoll()
(qui ne le fait pas). Une fois que vous avez dessiné tous les résultats attendus correspondant aux tâches que vous avez soumises, vous savez que tout est terminé.Permettez-moi de le répéter une fois de plus, car ce n'est pas évident à partir de l'interface: vous devez savoir combien de choses vous mettez dans le
CompletionService
afin de savoir combien de choses essayer de tirer. Cela est particulièrement important avec latake()
méthode: appelez-la une fois de plus et cela bloquera votre thread appelant jusqu'à ce qu'un autre thread soumette un autre travail à la même choseCompletionService
.Il y a quelques exemples montrant comment utiliser
CompletionService
dans le livre Java Concurrency in Practice .la source
CompletionService
Si vous voulez attendre la fin de l'exécution du service exécuteur, appelez
shutdown()
puis attendez Termination (unités, type d'unité) , par exempleawaitTermination(1, MINUTE)
. ExecutorService ne bloque pas sur son propre moniteur, vous ne pouvez donc pas utiliserwait
etc.la source
awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
stackoverflow.com/a/1250655/32453Vous pouvez attendre la fin des travaux à un certain intervalle:
Ou vous pouvez utiliser ExecutorService . soumettre ( Runnable ) et collecter les objets Future qu'il retourne et appeler get () sur chacun à son tour pour attendre qu'ils se terminent.
InterruptedException est extrêmement important à gérer correctement. C'est ce qui vous permet, à vous ou aux utilisateurs de votre bibliothèque, de terminer un long processus en toute sécurité.
la source
Utilisez simplement
Dans chaque fil
et comme barrière
la source
Cause racine de IllegalMonitorStateException :
À partir de votre code, vous venez d'appeler wait () sur ExecutorService sans posséder le verrou.
Le code ci-dessous corrigera
IllegalMonitorStateException
Suivez l'une des approches ci-dessous pour attendre la fin de toutes les tâches qui ont été soumises
ExecutorService
.Itérer à travers toutes les
Future
tâches à partirsubmit
deExecutorService
et vérifier l'état avec blocage de l'appelget()
sur l'Future
objetUtiliser invokeAll sur
ExecutorService
Utilisation de CountDownLatch
Utiliser ForkJoinPool ou newWorkStealingPool de
Executors
(depuis java 8)Arrêtez le pool comme recommandé dans la page de documentation d'Oracle
Si vous souhaitez attendre gracieusement la fin de toutes les tâches lorsque vous utilisez l'option 5 au lieu des options 1 à 4, modifiez
à
un
while(condition)
qui vérifie toutes les 1 minute.la source
Vous pouvez utiliser la
ExecutorService.invokeAll
méthode, il exécutera toutes les tâches et attendra que tous les threads aient terminé leur tâche.Voici javadoc complet
Vous pouvez également utiliser une version surchargée de cette méthode pour spécifier le délai d'expiration.
Voici un exemple de code avec
ExecutorService.invokeAll
la source
J'ai également la situation que j'ai un ensemble de documents à explorer. Je commence par un document initial "initial" qui doit être traité, ce document contient des liens vers d'autres documents qui doivent également être traités, etc.
Dans mon programme principal, je veux juste écrire quelque chose comme le suivant, où
Crawler
contrôle un tas de threads.La même situation se produirait si je voulais naviguer dans un arbre; je ferais apparaître le nœud racine, le processeur de chaque nœud ajouterait des enfants à la file d'attente si nécessaire, et un tas de threads traiterait tous les nœuds de l'arbre, jusqu'à ce qu'il n'y en ait plus.
Je n'ai rien trouvé dans la JVM que j'ai trouvé un peu surprenant. J'ai donc écrit une classe
ThreadPool
que l'on peut utiliser directement ou sous-classe pour ajouter des méthodes adaptées au domaine, par exempleschedule(Document)
. J'espère que ça aide!ThreadPool Javadoc | Maven
la source
Ajoutez tous les threads de la collection et soumettez-le à l'aide de
invokeAll
. Si vous pouvez utiliser lainvokeAll
méthode deExecutorService
, JVM ne passera pas à la ligne suivante tant que tous les threads ne seront pas terminés.Voici un bon exemple: invokeAll via ExecutorService
la source
Soumettez vos tâches dans le Runner , puis attendez d'appeler la méthode waitTillDone () comme ceci:
Pour l'utiliser, ajoutez cette dépendance gradle / maven:
'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
la source
Une alternative simple à cela est d'utiliser des threads avec join. Reportez-vous à: Rejoindre des discussions
la source
J'attendrai simplement que l'exécuteur se termine avec un délai spécifié que vous pensez qu'il convient aux tâches à accomplir.
la source
On dirait que vous avez besoin
ForkJoinPool
et utilisez le pool global pour exécuter des tâches.La beauté réside dans le fait
pool.awaitQuiescence
que la méthode bloquera l'utilisation du thread de l'appelant pour exécuter ses tâches, puis reviendra lorsqu'elle sera vraiment vide.la source