Créer des pools de threads en Java : différentes approches
Qu'est-ce qu'un pool de threads ?
Un pool de threads est un mécanisme qui permet de gérer un ensemble de threads réutilisables. Au lieu de créer et détruire des threads à chaque tâche, on maintient un reservoir de threads disponibles. Lorsqu'une tâche doit être exécutée, on récupère un thread du pool, on lui assigne la tâche, et une fois terminé, le thread retourne dans le pool. Cela évite la surcharge liée à la création et destruction répétée de threads.
Avantages :
- Optimisation de l'utilisation des ressources système
- Réduction du délai de réponse aux requêtes
- Meilleure administration des threads concurrents
Création d'un pool personnalisé
Pour créer un pool de threads personnalisé, on utilise la classe ThreadPoolExecutor avec les paramètres suivants :
- int corePoolSize,
- int maximumPoolSize,
- long keepAliveTime,
- TimeUnit unit,
- BlockingQueue workQueue,
- ThreadFactory threadFactory,
- RejectedExecutionHandler handler
Description de chaque paramètre :
int corePoolSize Nombre de threads permanents maintenus dans le pool
int maximumPoolSize Nombre maximum de threads que le pool peut contenir
long keepAliveTime Durée de vie des threads inactifs avant leur destruction
TimeUnit unit Unité de temps pour keepAliveTime (TimeUnit.XXX)
BlockingQueue<Runnable> workQueue File d'attente bloquante pour les tâches en attente
Types de files disponibles :
ArrayBlockingQueue: File bloquante bornée basée sur un tableau
LinkedBlockingQueue:File bloquante bornée basée sur une liste chaînée
SynchronousQueue:File bloquante sans stockage, transmission directe aux threads
PriorityBlockingQueue:File bloquante non bornée avec ordre de priorité
DelayQueue:File bloquante non bornée basée sur PriorityQueue, extraction différée
LinkedTransferQueue:File non bornée avec méthodes de transfert non bloquantes
LinkedBlockingDeque:File bloquante bidirectionnelle
ThreadFactory threadFactory Fabrique pour la création des threads
RejectedExecutionHandler handler Gestionnaire de rejet pour les tâches refusées
Quatre stratégies de rejet :
AbortPolicy:Rejet avec exception
CallerRunsPolicy:Exécution dans le thread appelant
DiscardOldestPolicy: Suppression de la tâche la plus ancienne
DiscardPolicy: Ignorement silencieux de la tâche
Pool de threads fixe
Crée un pool avec un nombre fixe de threads, permettant de contrôler la concurrance. Les tâches supplémentaires attendent dans la file d'attente.
ExecutorService threadPool = Executors.newFixedThreadPool(5);
Pool de threads cache
Crée un pool adaptatif qui peut ajouter ou supprimer des threads dynamiquement selon la charge de travail.
ExecutorService threadPool = Executors.newCachedThreadPool();
Pool à thread unique
Crée un pool contenant un seul thread, garantissant un ordre d'exécution FIFO (First In First Out).
ExecutorService threadPool = Executors.newSingleThreadExecutor();
Pool planifié avec délai
Crée un pool permettant l'exécution de tâches avec un délai определен.
ScheduledExecutorService planificateur = Executors.newScheduledThreadPool(5);
System.out.println("Avant exécution différée : "+new Date());
planificateur.schedule(()->{
System.out.println("Moment de l'exécution différée : "+new Date());},5L, TimeUnit.SECONDS);
Pool planifié mono-thread
Crée un pool planifié avec un seul thread pour les tâches différées.
ScheduledExecutorService planificateur = Executors.newSingleThreadScheduledExecutor();
Pool avec vol de travail
Crée un pool utilisant le mécanisme de work stealing (les threads peuvent voler des tâches d'autres threads). Note : disponible uniquement depuis JDK 1.8.
ExecutorService poolTravail = Executors.newWorkStealingPool();
for (int i = 0; i < 10; i++) {
final int numeroTache = i;
poolTravail.execute(()->{
System.out.println("Tâche "+numeroTache+" exécutée par : "+Thread.currentThread().getName());
});
}
while (!poolTravail.isTerminated()){
}