web-dev-qa-db-fra.com

Le programme ne se termine pas immédiatement lorsque toutes les tâches ExecutorService sont terminées

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.

22
alapeno

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.

28
yshavit

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.

6
Gray

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.)

3
Russell Zahniser

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) {
  ...
}
3
Danke Xie

Pour plusieurs threads de ExecutorService, la solution est ThreadPool.shutdown ();

0
Venkat Alugolu