web-dev-qa-db-fra.com

FixedThreadPool vs CachedThreadPool: le moindre de deux maux

J'ai un programme qui génère des threads (~ 5-150) qui effectuent un tas de tâches. A l’origine, j’utilisais un FixedThreadPool parce que cette question similaire suggérait qu’ils étaient mieux adaptés aux tâches plus longues et, compte tenu de ma connaissance très limitée du multithreading, j’envisageais la durée de vie moyenne des fils (plusieurs minutes) " longtemps vécu ".

Cependant, j'ai récemment ajouté la possibilité de générer des threads supplémentaires, ce qui me permet de dépasser la limite de threads définie. Dans ce cas, serait-il préférable de deviner et d'augmenter le nombre de threads que je peux autoriser ou de passer à un CachedThreadPool afin que je n'ai pas de threads perdus?

Les essayer tous les deux au préalable, il ne semble pas donc je suis enclin à aller avec le CachedThreadPool juste pour éviter le gaspillage. Cependant, la durée de vie des threads signifie-t-elle que je devrais plutôt choisir un FixedThreadPool et traiter uniquement les threads inutilisés? Cette question donne l'impression que ces fils supplémentaires ne sont pas gaspillés, mais j'apprécierais les éclaircissements.

83
Daniel

Un CachedThreadPool est exactement ce que vous devriez utiliser dans votre situation car il n'y a aucune conséquence négative à en utiliser un pour les threads de longue durée. Le commentaire dans la Java doc sur le fait que CachedThreadPools convient à des tâches courtes suggère simplement qu’ils sont particulièrement appropriés pour de tels cas, pas qu’ils ne peuvent ou ne doivent pas être utilisés pour des tâches impliquant des tâches longues.

Pour plus de précisions, Executors.newCachedThreadPool et Executors.newFixedThreadPool sont tous deux pris en charge par la même implémentation de pool de threads (au moins dans le JDK ouvert), mais avec des paramètres différents. Les différences sont juste le minimum, le maximum, le temps d'arrêt du thread 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>());
}

FixedThreadPool a ses avantages lorsque vous souhaitez travailler avec un nombre fixe de threads, car vous pouvez alors soumettre un nombre illimité de tâches au service exécuteur 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 toutefois que le seul problème que vous pourriez avoir avec CachedThreadPool concerne la limitation du nombre de threads exécutés simultanément. CachedThreadPool ne les limitera pas pour vous. Vous devrez donc peut-être écrire votre propre code pour vous assurer de ne pas exécuter trop de threads. Cela dépend vraiment de la conception de votre application et de la façon dont les tâches sont soumises au service de l’exécuteur.

100
Trevor Freeman

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

CachedThreadPool est plus dangereux que FixedThreadPool

Si votre application est très chargée et demande une faible latence, il est préférable de supprimer les deux options en raison des inconvénients ci-dessous.

  1. Nature non liée de la file d'attente des tâches: cela peut entraîner une insuffisance de mémoire ou une latence élevée
  2. Les longs threads entraîneront une perte de contrôle de CachedThreadPool lors de la création du thread.

Puisque vous savez que les deux sont des maux, le moindre mal ne fait pas le bien. Prefer ThreadPoolExecutor , qui fournit un contrôle granulaire sur de nombreux paramètres.

  1. Définissez la file d'attente des tâches en tant que file limitée pour avoir un meilleur contrôle
  2. Have right RejectionHandler - Votre propre gestionnaire RejectionHandler ou Default fourni par JDK
  3. Si vous avez quelque chose à faire avant/après la fin de la tâche, remplacez beforeExecute(Thread, Runnable) et afterExecute(Runnable, Throwable)
  4. Remplacer ThreadFactory si la personnalisation du fil est requise
  5. Contrôle de la taille dynamique du pool d'unités d'exécution au moment de l'exécution (question SE correspondante: pool d'unités dynamiques )
36
Ravindra babu

Donc, j'ai un programme qui génère des threads (~ 5-150) qui effectuent un tas de tâches.

Êtes-vous sûr de comprendre comment les threads sont réellement traités par votre système d'exploitation et le matériel de votre choix? Comment Java mappe les threads aux threads du système d'exploitation, comment cela mappe-t-il aux threads de la CPU, etc.? Je pose la question car la création de 150 threads dans ONE JRE n’a de sens que si vous avez d’énormes noyaux/threads de processeur, ce qui n’est probablement pas le cas. Selon le système d'exploitation et la RAM utilisés, la création de plus de n threads peut même entraîner l'arrêt de votre JRE à cause d'erreurs dans le MOO. Donc, vous devriez vraiment faire la distinction entre les threads et le travail à effectuer par ces threads, le nombre de travail que vous êtes même capable de traiter, etc.

Et c'est là le problème avec CachedThreadPool: Cela n'a pas de sens de mettre en file d'attente des travaux longs dans des threads qui ne peuvent pas réellement s'exécuter, car vous ne disposez que de 2 cœurs de processeur 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. Cela est tout simplement impossible si vous ne disposez que de 2 cœurs de processeur, à moins que vos threads n'attendent des E/S ou tout le temps. Mais même dans ce cas, beaucoup de threads créeraient beaucoup d'E/S ...

Et ce problème ne se produit pas avec FixedThreadPool, créé avec par exemple. 2 + n threads, où n est bien sûr raisonnable, car avec ce matériel, les ressources de système d'exploitation sont utilisées avec beaucoup moins de temps système pour la gestion de threads qui ne peuvent pas être exécutés de toute façon.

5
Thorsten Schöning