Je mets un tas d'objets exécutables dans un ExecutorService:
// simplified content of main method
ExecutorService threadPool = Executors.newCachedThreadPool();
for(int i = 0; i < workerCount; i++) {
threadPool.execute(new Worker());
}
Je m'attendrais à ce que mon programme/processus s'arrête immédiatement après que tous les travailleurs sont terminés. Mais selon mon journal, cela prend encore 20 à 30 secondes. Les travailleurs n'allouent aucune ressource. En fait, ils ne font rien pour le moment.
Ne vous méprenez pas, ce n'est pas un problème crucial pour moi, j'essaie simplement de comprendre ce qui se passe et je me demande s'il s'agit d'un comportement normal.
Executors.newCachedThreadPool()
utilise Executors.defaultThreadFactory()
pour sa ThreadFactory
. Les javadocs de defaultThreadFactory
indiquent que "chaque nouveau thread est créé en tant que thread non-démon" (soulignement ajouté). Ainsi, les threads créés pour la variable newCachedThreadPool
ne sont pas des démons. Cela signifie qu'ils empêcheront la JVM de sortir naturellement (par «naturellement», je veux dire que vous pouvez toujours appeler System.exit(1)
ou tuer le programme pour que la JVM s'arrête).
La raison pour laquelle l'application se termine est que chaque thread créé dans la variable newCachedThreadPool
expire et se ferme après un certain temps d'inactivité. Lorsque le dernier d'entre eux se ferme, si votre application n'a plus de threads non démon, elle se ferme.
Vous pouvez (et devriez) fermer la ExecutorService
manuellement via shutdown
ou shutdownNow
.
Voir aussi le JavaDoc pour Thread , qui parle de daemon-ness.
Je m'attendrais à ce que mon programme/processus s'arrête immédiatement après que tous les travailleurs ont terminé. Mais selon mon journal, cela prend encore 20 à 30 secondes. Les travailleurs n'allouent aucune ressource. En fait, ils ne font rien pour le moment.
Le problème est que vous n'éteignez pas votre ExecutorService
. Une fois que vous avez soumis tous les travaux au service, vous devez arrêter le service, sinon la machine virtuelle Java ne sera pas fermée à moins que tous les threads qu'il contient ne soient des démons. Si vous n'arrêtez pas le pool de threads, tous les threads associés à la variable ExecutorService
, là encore s'il ne s'agit pas d'un démon, empêcheront la machine virtuelle Java de se terminer. Si vous avez soumis des tâches à un pool de threads mis en cache, vous devrez attendre que les threads expirent et soient récoltés avant la fin de la JVM.
ExecutorService threadPool = Executors.newCachedThreadPool();
for(int i = 0; i < workerCount; i++) {
threadPool.execute(new Worker());
}
// you _must_ do this after submitting all of your workers
threadPool.shutdown();
Il est fort probable que le démarrage des threads en tant que démon pas ce que vous voulez faire, car votre application peut s'arrêter avant / les tâches sont terminées et toutes les tâches seront terminées immédiatement à ce moment-là. Je viens de faire un audit rapide et sur les 178 fois où nous utilisons des classes ExecutorService
dans notre code de production, seulement 2 d’entre elles ont été lancées en tant que threads de démon. Les autres sont correctement arrêtés.
Si vous devez forcer une ExecutorService
à s’arrêter à la fermeture de l’application, utilisez shutdownNow()
avec le traitement approprié des indicateurs d’interruption de thread.
Depuis le javadoc pour Executors.newCachedThreadPool()
:
Les threads qui n'ont pas été utilisés pendant soixante secondes sont terminés et supprimés du cache.
C'est généralement une bonne idée d'appeler shutdown()
sur une ExecutorService
si vous savez qu'aucune nouvelle tâche ne lui sera soumise. Toutes les tâches de la file d'attente seront alors terminées, mais le service sera immédiatement arrêté.
(Sinon, si vous ne vous souciez pas de savoir si toutes les tâches sont terminées - par exemple, si elles gèrent des calculs en arrière-plan qui ne sont plus pertinents une fois que votre interface utilisateur principale a disparu - vous pouvez créer une ThreadFactory
qui définit tous les threads de ce pool démon.)
En gros, sur un ExecutorService, vous appelez shutdown () puis waitTermination ():
ExecutorService taskExecutor = Executors.newFixedThreadPool(4);
while(...) {
taskExecutor.execute(new MyTask());
}
taskExecutor.shutdown();
try {
taskExecutor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
} catch (InterruptedException e) {
...
}
Pour plusieurs threads de ExecutorService, la solution est ThreadPool.shutdown ();