110 votes

FixedThreadPool vs CachedThreadPool : le moindre des deux maux

J'ai un programme qui génère des threads (~5-150) qui effectuent un certain nombre de tâches. À l'origine, j'ai utilisé un FixedThreadPool parce que cette question similaire a suggéré qu'ils étaient mieux adaptés aux tâches de longue durée et, avec ma connaissance très limitée du multithreading, j'ai considéré la durée de vie moyenne des threads (plusieurs minutes) ". a vécu longtemps ".

Cependant, j'ai récemment ajouté la possibilité de créer des fils supplémentaires, ce qui me fait dépasser la limite de fils que j'ai fixée. Dans ce cas, serait-il mieux de deviner et d'augmenter le nombre de threads que je peux autoriser ou de passer à un CachedThreadPool pour que je n'aie pas de fils gaspillés ?

En les essayant tous les deux de manière préliminaire, il n'y a pas semblent d'être une différence, donc je suis enclin à aller avec le CachedThreadPool juste pour éviter le gaspillage. Cependant, est-ce que la durée de vie des fils signifie que je devrais plutôt choisir une FixedThreadPool et juste gérer les fils inutilisés ? Cette question donne l'impression que ces fils supplémentaires ne sont pas gaspillés, mais j'apprécierais une clarification.

123voto

increment1 Points 3144

A CachedThreadPool semble approprié à votre situation car il n'y a pas de conséquence négative à l'utilisation d'un pool pour les threads longs. Le commentaire dans la documentation java sur les CachedThreadPools qui conviennent aux tâches courtes suggère simplement qu'ils sont particulièrement appropriés pour de tels cas, et non qu'ils ne peuvent pas être utilisés pour des tâches longues.

Le principal souci avec un CachedThreadPool est qu'il créera jusqu'à Integer.MAX_VALUE nombre de threads car il créera toujours un nouveau thread si un thread inutilisé n'existe pas dans le cache. Ainsi, si vous avez des tâches de longue durée, il est plus probable que vous puissiez augmenter le nombre de threads simultanés plus que vous ne le souhaitez puisque ce type de pool de threads ne limitera pas le nombre de threads exécutés simultanément. Cela ne semble pas être un problème pour votre cas d'utilisation tel que décrit, mais c'est quelque chose dont il faut être conscient.

Pour approfondir la différence entre un CachedThreadPool et un FixedThreadPool , Exécuteurs.newCachedThreadPool et Exécuteurs.newFixedThreadPool sont toutes deux soutenues par la même implémentation de pool de threads (au moins dans le JDK ouvert) via une instance de ThreadPoolExecutor mais avec des paramètres différents. Les différences sont simplement le minimum et le maximum de threads, le temps de destruction des threads et le type de file d'attente.

public static ExecutorService newFixedThreadPool(int nThreads) {
     return new ThreadPoolExecutor(nThreads, nThreads,
                                   0L, TimeUnit.MILLISECONDS,
                                   new LinkedBlockingQueue<Runnable>());
 }

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                 60L, TimeUnit.SECONDS,
                                 new SynchronousQueue<Runnable>());
}

A FixedThreadPool a ses avantages lorsque vous souhaitez effectivement travailler avec un nombre fixe de threads, car vous pouvez alors soumettre n'importe quel nombre de tâches au service d'exécution tout en sachant que le nombre de threads sera maintenu au niveau que vous avez spécifié. Si vous souhaitez explicitement augmenter le nombre de threads, ce n'est pas le choix approprié.

Cela signifie cependant que le seul problème que vous pouvez avoir avec le CachedThreadPool est de limiter le nombre de threads qui fonctionnent simultanément. Le site CachedThreadPool ne les limitera pas pour vous, vous devrez donc écrire votre propre code pour vous assurer que vous n'exécutez pas trop de threads, ce que vous pouvez faire relativement facilement en instanciant votre propre ThreadPoolExecutor avec les caractéristiques de comportement que vous souhaitez. Cela dépend vraiment de la conception de votre application et de la manière dont les tâches sont soumises au service d'exécution.

59voto

Ravindra babu Points 5571

Les deux sites FixedThreadPool et CachedThreadPool sont des maux dans les applications très chargées.

CachedThreadPool est plus dangereux que FixedThreadPool

Si votre application est très chargée et exige une faible latence, il est préférable de renoncer aux deux options en raison des inconvénients suivants

  1. La nature illimitée de la file d'attente des tâches : elle peut provoquer un manque de mémoire ou une latence élevée.
  2. Des fils longs provoquent CachedThreadPool de devenir incontrôlable sur la création de fils

Puisque vous savez que les deux sont des maux, le moindre mal ne fait pas de bien. Préférez ThreadPoolExecutor qui permet un contrôle granulaire de nombreux paramètres.

  1. Définir la file d'attente des tâches comme file d'attente bornée pour avoir un meilleur contrôle.
  2. Avoir le bon RejectionHandler - Votre propre RejectionHandler ou les gestionnaires par défaut fournis par le JDK.
  3. Si vous avez quelque chose à faire avant ou après l'achèvement de la tâche, passez outre. beforeExecute(Thread, Runnable) et afterExecute(Runnable, Throwable)
  4. Remplacer ThreadFactory si la personnalisation du fil est nécessaire
  5. Contrôle de la taille du pool de threads de manière dynamique au moment de l'exécution (question SE connexe) : Pool de threads dynamique )

5voto

J'ai donc un programme qui génère des threads (~5-150) qui effectuent un certain nombre de tâches.

Êtes-vous sûr de comprendre comment les threads sont traités par votre système d'exploitation et le matériel de votre choix ? Comment Java fait correspondre les threads aux threads du système d'exploitation, comment il fait correspondre les threads aux threads du processeur, etc. Je pose la question parce que la création de 150 threads dans UN JRE n'a de sens que si vous disposez d'un grand nombre de cœurs/threads CPU, ce qui n'est probablement pas le cas. En fonction du système d'exploitation et de la mémoire vive utilisée, la création de plus de n threads peut même entraîner l'arrêt de votre JRE en raison d'erreurs OOM. Vous devez donc vraiment faire la distinction entre les threads et le travail à effectuer par ces threads, le nombre de tâches que vous êtes même capable de traiter, etc.

Et c'est là le problème du CachedThreadPool : il n'est pas logique de mettre en file d'attente un travail de longue haleine dans des threads qui ne peuvent en fait pas être exécutés parce que vous n'avez que deux cœurs de CPU capables de traiter ces threads. Si vous vous retrouvez avec 150 threads planifiés, vous risquez de créer une surcharge inutile pour les planificateurs utilisés dans Java et le système d'exploitation pour les traiter simultanément. C'est tout simplement impossible si vous ne disposez que de deux cœurs de processeur, à moins que vos threads n'attendent en permanence des entrées/sorties ou autres. Mais même dans ce cas, un grand nombre de threads créerait un grand nombre d'E/S...

Et ce problème ne se produit pas avec le FixedThreadPool, créé avec par exemple 2+n threads, où n est raisonnablement bas bien sûr, parce qu'avec cela les ressources du matériel et du système d'exploitation sont utilisées avec beaucoup moins de surcharge pour gérer des threads qui ne peuvent pas fonctionner de toute façon.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X