newCachedThreadPool()
contre newFixedThreadPool()
Quand dois-je utiliser l'un ou l'autre? Quelle stratégie est la meilleure en termes d'utilisation des ressources?
newCachedThreadPool()
contre newFixedThreadPool()
Quand dois-je utiliser l'un ou l'autre? Quelle stratégie est la meilleure en termes d'utilisation des ressources?
Je pense que la documentation explique assez bien la différence et l'utilisation de ces deux fonctions:
Crée un pool de threads qui réutilise un nombre fixe de threads fonctionnant à partir d'une file d'attente partagée illimitée. À tout moment, la plupart des threads nThreads seront des tâches de traitement actives. Si des tâches supplémentaires sont soumises lorsque tous les threads sont actifs, ils attendront dans la file d'attente jusqu'à ce qu'un thread soit disponible. Si un thread se termine en raison d'un échec lors de l'exécution avant l'arrêt, un nouveau prendra sa place si nécessaire pour exécuter les tâches suivantes. Les threads du pool existeront jusqu'à ce qu'il soit explicitement arrêté.
Crée un pool de threads qui crée de nouveaux threads selon les besoins, mais réutilise les threads précédemment construits lorsqu'ils sont disponibles. Ces pools améliorent généralement les performances des programmes qui exécutent de nombreuses tâches asynchrones de courte durée. Les appels à exécuter réutiliseront les threads précédemment construits s'ils sont disponibles. Si aucun thread existant n'est disponible, un nouveau thread sera créé et ajouté au pool. Les threads qui n'ont pas été utilisés pendant soixante secondes sont arrêtés et supprimés du cache. Ainsi, un pool qui reste inactif suffisamment longtemps ne consommera aucune ressource. Notez que des pools avec des propriétés similaires mais des détails différents (par exemple, des paramètres de délai d'expiration) peuvent être créés à l'aide des constructeurs ThreadPoolExecutor.
En termes de ressources, le newFixedThreadPool
maintiendra tous les threads en cours d'exécution jusqu'à ce qu'ils soient explicitement terminés. Dans les newCachedThreadPool
threads qui n'ont pas été utilisés pendant soixante secondes sont arrêtés et supprimés du cache.
Compte tenu de cela, la consommation de ressources dépendra beaucoup de la situation. Par exemple, si vous avez un grand nombre de tâches de longue durée, je suggérerais le FixedThreadPool
. En ce qui concerne le CachedThreadPool
, la documentation dit que "ces pools amélioreront généralement les performances des programmes qui exécutent de nombreuses tâches asynchrones de courte durée".
newCachedThreadPool
peut causer de sérieux problèmes car vous laissez tout le contrôle authread pool
et lorsque le service fonctionne avec d'autres dans le même hôte , ce qui peut provoquer un crash des autres en raison d'une attente prolongée du processeur. Je pense donc que celanewFixedThreadPool
peut être plus sûr dans ce genre de scénario. Cet article clarifie également les différences les plus marquantes entre eux.Pour compléter les autres réponses, je voudrais citer Effective Java, 2e édition, par Joshua Bloch, chapitre 10, point 68:
la source
Si vous regardez le code source , vous verrez, ils appellent ThreadPoolExecutor. en interne et définir leurs propriétés. Vous pouvez créer le vôtre pour avoir une meilleure maîtrise de vos besoins.
la source
Si vous n'êtes pas préoccupé par une file d'attente illimitée de tâches appelables / exécutables , vous pouvez utiliser l'une d'entre elles. Comme suggéré par bruno, je préfère aussi
newFixedThreadPool
ànewCachedThreadPool
plus de ces deux.Mais ThreadPoolExecutor offre des fonctionnalités plus flexibles par rapport à l'un
newFixedThreadPool
ou l' autrenewCachedThreadPool
Avantages:
Vous avez un contrôle total sur la taille de BlockingQueue . Ce n'est pas illimité, contrairement aux deux options précédentes. Je n'obtiendrai pas une erreur de mémoire insuffisante en raison d'un énorme tas de tâches appelables / exécutables en attente lorsqu'il y a des turbulences inattendues dans le système.
Vous pouvez mettre en œuvre une stratégie de gestion des rejets personnalisée OU utiliser l'une des stratégies:
Par défaut
ThreadPoolExecutor.AbortPolicy
, le gestionnaire lève une exception RejectedExecutionException lors du rejet.Dans
ThreadPoolExecutor.CallerRunsPolicy
, le thread qui invoque s'exécute lui-même exécute la tâche. Cela fournit un mécanisme de contrôle de rétroaction simple qui ralentira la vitesse à laquelle de nouvelles tâches sont soumises.Dans
ThreadPoolExecutor.DiscardPolicy
, une tâche qui ne peut pas être exécutée est simplement supprimée.Dans
ThreadPoolExecutor.DiscardOldestPolicy
, si l'exécuteur n'est pas arrêté, la tâche en tête de la file d'attente de travail est supprimée, puis l'exécution est relancée (ce qui peut échouer à nouveau, provoquant la répétition de cette opération.)Vous pouvez implémenter une fabrique de threads personnalisée pour les cas d'utilisation ci-dessous:
la source
C'est vrai, ce
Executors.newCachedThreadPool()
n'est pas un excellent choix pour le code serveur qui traite plusieurs clients et demandes simultanées.Pourquoi? Il y a essentiellement deux problèmes (liés) avec cela:
C'est illimité, ce qui signifie que vous ouvrez la porte à quiconque pour paralyser votre JVM en injectant simplement plus de travail dans le service (attaque DoS). Les threads consomment une quantité non négligeable de mémoire et augmentent également la consommation de mémoire en fonction de leur travail en cours, il est donc assez facile de renverser un serveur de cette façon (sauf si vous avez d'autres disjoncteurs en place).
Le problème illimité est exacerbé par le fait que l'exécuteur est dirigé par un,
SynchronousQueue
ce qui signifie qu'il y a un transfert direct entre le donneur de tâches et le pool de threads. Chaque nouvelle tâche créera un nouveau thread si tous les threads existants sont occupés. C'est généralement une mauvaise stratégie pour le code serveur. Lorsque le processeur est saturé, les tâches existantes prennent plus de temps à se terminer. Pourtant, plus de tâches sont soumises et plus de threads créés, de sorte que les tâches prennent de plus en plus de temps à terminer. Lorsque le processeur est saturé, plus de threads n'est certainement pas ce dont le serveur a besoin.Voici mes recommandations:
Utilisez un pool de threads de taille fixe Executors.newFixedThreadPool ou un ThreadPoolExecutor. avec un nombre maximum de threads défini;
la source
La
ThreadPoolExecutor
classe est l'implémentation de base des exécuteurs renvoyés par de nombreusesExecutors
méthodes de fabrique. Alors abordons les pools de threads fixes et mis en cache duThreadPoolExecutor
point de vue de.ThreadPoolExecutor
Le constructeur principal de cette classe ressemble à ceci:
Taille de la piscine principale
Le
corePoolSize
détermine la taille minimale du pool de threads cible. L'implémentation conserverait un pool de cette taille même s'il n'y a aucune tâche à exécuter.Taille maximale de la piscine
Le
maximumPoolSize
est le nombre maximum de threads qui peuvent être actifs à la fois.Une fois que le pool de threads augmente et devient plus grand que le
corePoolSize
seuil, l'exécuteur peut mettre fin aux threads inactifs et atteindre àcorePoolSize
nouveau. SiallowCoreThreadTimeOut
est vrai, l'exécuteur peut même mettre fin aux threads du pool de base s'ils étaient inactifs au-delà dukeepAliveTime
seuil.Donc, l'essentiel est que si les threads restent inactifs au-delà du
keepAliveTime
seuil, ils peuvent se terminer car il n'y a pas de demande pour eux.File d'attente
Que se passe-t-il lorsqu'une nouvelle tâche arrive et que tous les threads principaux sont occupés? Les nouvelles tâches seront mises en file d'attente dans cette
BlockingQueue<Runnable>
instance. Lorsqu'un thread devient libre, l'une de ces tâches en file d'attente peut être traitée.Il existe différentes implémentations de l'
BlockingQueue
interface en Java, nous pouvons donc implémenter différentes approches de mise en file d'attente telles que:File d'attente limitée : les nouvelles tâches seraient mises en file d'attente dans une file d'attente de tâches limitée.
File d'attente illimitée : les nouvelles tâches seraient mises en file d'attente dans une file d'attente de tâches illimitée. Ainsi, cette file d'attente peut augmenter autant que la taille du tas le permet.
Transfert synchrone : nous pouvons également utiliser
SynchronousQueue
pour mettre en file d'attente les nouvelles tâches. Dans ce cas, lors de la mise en file d'attente d'une nouvelle tâche, un autre thread doit déjà attendre cette tâche.Soumission de travail
Voici comment
ThreadPoolExecutor
exécute une nouvelle tâche:corePoolSize
threads sont en cours d'exécution, essaie de démarrer un nouveau thread avec la tâche donnée comme premier travail.BlockingQueue#offer
méthode. Laoffer
méthode ne bloquera pas si la file d'attente est pleine et retourne immédiatementfalse
.offer
retournefalse
), il essaie alors d'ajouter un nouveau thread au pool de threads avec cette tâche comme premier travail.RejectedExecutionHandler
.La principale différence entre les pools de threads fixes et mis en cache se résume à ces trois facteurs:
Pool de threads fixe
Voici comment ça
Excutors.newFixedThreadPool(n)
marche:Comme vous pouvez le voir:
OutOfMemoryError
.Un pool de threads de taille fixe semble être un bon candidat lorsque nous allons limiter le nombre de tâches simultanées à des fins de gestion des ressources .
Par exemple, si nous allons utiliser un exécuteur pour gérer les demandes du serveur Web, un exécuteur fixe peut gérer les rafales de demandes plus raisonnablement.
Pour une gestion encore meilleure des ressources, il est fortement recommandé de créer une configuration personnalisée
ThreadPoolExecutor
avec uneBlockingQueue<T>
implémentation limitée couplée à une solution raisonnableRejectedExecutionHandler
.Pool de threads mis en cache
Voici comment ça
Executors.newCachedThreadPool()
marche:Comme vous pouvez le voir:
Integer.MAX_VALUE
. En pratique, le pool de threads est illimité.SynchronousQueue
échoue toujours quand il n'y a personne à l'autre bout pour l'accepter!Utilisez-le lorsque vous avez beaucoup de tâches prévisibles à court terme.
la source
Vous devez utiliser newCachedThreadPool uniquement lorsque vous avez des tâches asynchrones de courte durée comme indiqué dans Javadoc, si vous soumettez des tâches qui prennent plus de temps à traiter, vous finirez par créer trop de threads. Vous pouvez atteindre 100% du processeur si vous soumettez des tâches de longue durée à un rythme plus rapide à newCachedThreadPool ( http://rashcoder.com/be-careful- while-using-executors-newcachedthreadpool/ ).
la source
Je fais quelques tests rapides et j'ai les résultats suivants:
1) si vous utilisez SynchronousQueue:
Une fois que les threads atteignent la taille maximale, tout nouveau travail sera rejeté à l'exception comme ci-dessous.
2) si vous utilisez LinkedBlockingQueue:
Les threads n'augmentent jamais de la taille minimale à la taille maximale, ce qui signifie que le pool de threads est de taille fixe comme taille minimale.
la source