Il semble impossible de créer un pool de threads mis en cache avec une limite au nombre de threads qu'il peut créer.
Voici comment static Executors.newCachedThreadPool est implémenté dans la bibliothèque Java standard:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
Donc, en utilisant ce modèle pour continuer à créer un pool de threads mis en cache de taille fixe:
new ThreadPoolExecutor(0, 3, 60L, TimeUnit.SECONDS, new SynchronusQueue<Runable>());
Maintenant, si vous utilisez ceci et soumettez 3 tâches, tout ira bien. La soumission de toute autre tâche entraînera le rejet d'exceptions d'exécution.
Essayer ceci:
new ThreadPoolExecutor(0, 3, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runable>());
Cela entraînera l'exécution séquentielle de tous les threads. Par exemple, le pool de threads ne créera jamais plus d'un thread pour gérer vos tâches.
Il s'agit d'un bogue dans la méthode d'exécution de ThreadPoolExecutor? Ou peut-être que c'est intentionnel? Ou il y a un autre moyen?
Edit: Je veux quelque chose d'exactement comme le pool de threads mis en cache (il crée des threads à la demande, puis les tue après un certain délai) mais avec une limite sur le nombre de threads qu'il peut créer et la possibilité de continuer à mettre en file d'attente des tâches supplémentaires une fois qu'il a atteint sa limite de thread. Selon la réponse de sjlee, cela est impossible. En regardant la méthode execute () de ThreadPoolExecutor, c'est en effet impossible. J'aurais besoin de sous-classer ThreadPoolExecutor et de surcharger execute () un peu comme SwingWorker, mais ce que SwingWorker fait dans son execute () est un hack complet.
la source
Réponses:
ThreadPoolExecutor a les plusieurs comportements clés suivants, et vos problèmes peuvent être expliqués par ces comportements.
Lorsque les tâches sont soumises,
Dans le premier exemple, notez que SynchronousQueue a essentiellement une taille de 0. Par conséquent, dès que vous atteignez la taille maximale (3), la politique de rejet entre en jeu (# 4).
Dans le deuxième exemple, la file d'attente de choix est une LinkedBlockingQueue qui a une taille illimitée. Par conséquent, vous êtes coincé avec le comportement n ° 2.
Vous ne pouvez pas vraiment bricoler beaucoup avec le type mis en cache ou le type fixe, car leur comportement est presque complètement déterminé.
Si vous souhaitez avoir un pool de threads limité et dynamique, vous devez utiliser une taille de cœur positive et une taille maximale combinée à une file d'attente de taille finie. Par exemple,
Addendum : c'est une réponse assez ancienne, et il semble que JDK a changé son comportement en ce qui concerne la taille de cœur de 0. Depuis JDK 1.6, si la taille de cœur est de 0 et que le pool n'a pas de threads, le ThreadPoolExecutor ajoutera un thread pour exécuter cette tâche. Par conséquent, la taille de cœur de 0 est une exception à la règle ci-dessus. Merci Steve d' avoir porté cela à mon attention.
la source
allowCoreThreadTimeOut
pour rendre cette réponse parfaite. Voir la réponse de @ user1046052Sauf si j'ai manqué quelque chose, la solution à la question initiale est simple. Le code suivant implémente le comportement souhaité comme décrit par l'affiche d'origine. Il engendrera jusqu'à 5 threads pour travailler sur une file d'attente illimitée et les threads inactifs se termineront après 60 secondes.
la source
Eu le même problème. Puisqu'aucune autre réponse ne met tous les problèmes ensemble, j'ajoute le mien:
Il est maintenant clairement écrit dans la documentation : si vous utilisez une file d'attente qui ne bloque pas (
LinkedBlockingQueue
) le paramètre max threads n'a aucun effet, seuls les threads principaux sont utilisés.alors:
Cet exécuteur testamentaire a:
Aucun concept de threads maximum car nous utilisons une file d'attente illimitée. C'est une bonne chose car une telle file d'attente peut amener l'exécuteur à créer un nombre massif de threads supplémentaires non essentiels s'il suit sa politique habituelle.
Une file d'attente de taille maximale
Integer.MAX_VALUE
.Submit()
jetteraRejectedExecutionException
si le nombre de tâches en attente dépasseInteger.MAX_VALUE
. Pas sûr que nous allons d'abord manquer de mémoire ou cela se produira.Possède 4 fils de base. Les threads de base inactifs se terminent automatiquement s'ils sont inactifs pendant 5 secondes.Donc, oui, strictement à la demande, le nombre peut être modifié à l'aide de la
setThreads()
méthode.S'assure que le nombre minimum de threads principaux n'est jamais inférieur à un, sinon
submit()
rejettera chaque tâche. Puisque les threads principaux doivent être> = max threads, la méthodesetThreads()
définit également max threads, bien que le paramètre max threads soit inutile pour une file d'attente illimitée.la source
Dans votre premier exemple, les tâches suivantes sont rejetées car il
AbortPolicy
s'agit de la valeur par défautRejectedExecutionHandler
. Le ThreadPoolExecutor contient les stratégies suivantes, que vous pouvez modifier via lasetRejectedExecutionHandler
méthode:Il semble que vous vouliez un pool de threads mis en cache avec un CallerRunsPolicy.
la source
Aucune des réponses ici n'a résolu mon problème, qui concernait la création d'un nombre limité de connexions HTTP à l'aide du client HTTP d'Apache (version 3.x). Puisqu'il m'a fallu quelques heures pour trouver une bonne configuration, je vais partager:
Cela crée un
ThreadPoolExecutor
qui commence par cinq et contient un maximum de dix threads exécutés simultanément en utilisantCallerRunsPolicy
pour l'exécution.la source
Par le Javadoc pour ThreadPoolExecutor:
(Soulignez le mien.)
La réponse de jitter est ce que vous voulez, bien que la mienne réponde à votre autre question. :)
la source
il y a une autre option. Au lieu d'utiliser le nouveau SynchronousQueue, vous pouvez également utiliser n'importe quelle autre file d'attente, mais vous devez vous assurer que sa taille est de 1, ce qui forcera executorservice à créer un nouveau thread.
la source
Il ne semble pas que l'une des réponses réponde réellement à la question - en fait, je ne vois pas de moyen de le faire - même si vous sous-classez de PooledExecutorService puisque la plupart des méthodes / propriétés sont privées, par exemple en faisant addIfUnderMaximumPoolSize a été protégé, vous pouvez procédez comme suit:
Le plus proche que j'ai obtenu était celui-ci - mais même ce n'est pas une très bonne solution
ps pas testé le ci-dessus
la source
Voici une autre solution. Je pense que cette solution se comporte comme vous le souhaitez (mais pas fier de cette solution):
la source
C'est ce que vous voulez (du moins je suppose). Pour une explication, consultez la réponse de Jonathan Feinberg
Executors.newFixedThreadPool(int n)
la source
Vous pouvez utiliser
ThreadPoolExecutor
comme suggéré par @sjleeVous pouvez contrôler dynamiquement la taille du pool. Jetez un œil à cette question pour plus de détails:
Pool de threads dynamiques
OU
Vous pouvez utiliser l' API newWorkStealingPool , qui a été introduite avec java 8.
Par défaut, le niveau de parallélisme est défini sur le nombre de cœurs de processeur de votre serveur. Si vous avez un serveur CPU à 4 cœurs, la taille du pool de threads serait de 4. Cette API renvoie le
ForkJoinPool
type deExecutorService
et permet le vol de travail des threads inactifs en volant les tâches des threads occupés dans ForkJoinPool.la source
Le problème a été résumé comme suit:
Avant de pointer vers la solution, je vais expliquer pourquoi les solutions suivantes ne fonctionnent pas:
Cela ne mettra en file d'attente aucune tâche lorsque la limite de 3 est atteinte car SynchronousQueue, par définition, ne peut contenir aucun élément.
Cela ne créera pas plus d'un seul thread car ThreadPoolExecutor ne crée que des threads dépassant le corePoolSize si la file d'attente est pleine. Mais LinkedBlockingQueue n'est jamais plein.
Cela ne réutilisera pas les threads tant que le corePoolSize n'a pas été atteint car ThreadPoolExecutor augmente le nombre de threads jusqu'à ce que corePoolSize soit atteint même si les threads existants sont inactifs. Si vous pouvez vivre avec cet inconvénient, c'est la solution la plus simple au problème. C'est aussi la solution décrite dans "Java Concurrency in Practice" (note de bas de page p172).
La seule solution complète au problème décrit semble être celle consistant à remplacer la
offer
méthode de la file d'attente et à écrire unRejectedExecutionHandler
comme expliqué dans les réponses à cette question: Comment faire en sorte que ThreadPoolExecutor augmente les threads au maximum avant de mettre en file d'attente?la source
Cela fonctionne pour Java8 + (et autres, pour l'instant ..)
où 3 est la limite du nombre de threads et 5 est le délai d'expiration pour les threads inactifs.
Si vous voulez vérifier si cela fonctionne vous - même , voici le code pour faire le travail:
la sortie du code ci-dessus pour moi est
la source