Lors du développement d'une application multi-thread, nous devons généralement gérer l'organisation du travail des threads. Plus notre application est grande et plus nous avons besoin de threads pour les tâches multithreads, plusExécutableobjets que nous créons.

Il faut noter ici que la création d'un thread en Java est une opération assez coûteuse. Si nous créons une nouvelle instance du thread à chaque fois pour effectuer une opération, nous aurons de gros problèmes de performances et, par conséquent, de santé de l'application.

Un pool de threads et ThreadPoolExecutor viennent ici à notre aide.

Un pool de threads est un ensemble de threads pré-initialisés. Sa taille peut être fixe ou variable.

S'il y a plus de tâches que de threads, les tâches attendent dans une file d'attente de tâches. Le nième thread du pool prend une tâche dans la file d'attente et, une fois terminé, le thread sélectionne une nouvelle tâche dans la file d'attente. Une fois que toutes les tâches de la file d'attente sont exécutées, les threads restent actifs et attendent de nouvelles tâches. Lorsque de nouvelles tâches apparaissent, les threads commencent également à les exécuter.

ThreadPoolExecutor

À partir de Java 5, le framework Executor a acquis une solution multithreading. En général, il comporte de nombreux composants et son objectif est de nous aider à gérer efficacement les files d'attente et les pools de threads.

Les principales interfaces sont Executor et ExecutorService .

Executor est une interface avec une seule méthode void execute(Runnable runnable).

Lorsque vous passez une tâche à une implémentation de cette méthode, sachez qu'elle sera exécutée de manière asynchrone à l'avenir.

ExecutorService — Une interface qui étend l' interface Executor , en ajoutant des fonctionnalités pour l'exécution de tâches. Il dispose également de méthodes pour interrompre une tâche en cours d'exécution et mettre fin au pool de threads.

ThreadPoolExecutor implémente les interfaces Executor et ExecutorService et sépare la création de tâche de l'exécution de tâche. Nous devons implémenter des objets Runnable et les envoyer à un exécuteur. Le ThreadPoolExecutor est alors responsable de l'exécution des tâches, de la création et de l'utilisation des threads.

Une fois qu'une tâche est envoyée pour exécution, un thread existant dans le pool est utilisé. Cela améliore les performances. Cela résout le problème du gaspillage de ressources lors de la création et de l'initialisation d'un nouveau thread, puis à nouveau lors de la récupération de place une fois que nous en avons terminé avec le thread.

ThreadPoolExecutor a 4 constructeurs :


ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime, 
TimeUnit unit, 
BlockingQueue<Runnable> workQueue)
    

ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler)
    

ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime, 
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory)
    

ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime, 
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory, 
RejectedExecutionHandler handler)
    

Le constructeur ThreadPoolExecutor a les paramètres suivants :

corePoolSizecorePoolSize Ce paramètre indique combien de threads seront prêts (démarrés) au démarrage du service d'exécuteur.
maximumPoolSize Le nombre maximal de threads qu'un service d'exécuteur peut créer.
keepAliveTime Le temps qu'un thread libéré continuera à vivre avant d'être détruit si le nombre de threads est supérieur àcorePoolSizecorePoolSize. Les unités de temps sont spécifiées dans le paramètre suivant.
unité Unités de temps (heures, minutes, secondes, millisecondes, etc.).
workQueue Mise en place d'une file d'attente pour les tâches.
gestionnaire Gestionnaire pour les tâches qui ne peuvent pas être terminées.
filFactory Un objet qui crée de nouveaux threads à la demande. L'utilisation de fabriques de threads rend les appels à un nouveau thread indépendants du matériel, permettant aux applications d'utiliser des sous-classes de threads spéciales, des priorités, etc.

Création d'un ThreadPoolExecutor

La classe utilitaire Executors peut simplifier la création d'un ThreadPoolExecutor . Les méthodes de cette classe utilitaire nous aident à préparer unThreadPoolExecutorobjet.

newFixedThreadPool — Crée un pool de threads qui réutilise un nombre fixe de threads pour exécuter n'importe quel nombre de tâches.

ExecutorService executor = Executors.newFixedThreadPool(10);
                    
newWorkStealingPool — Crée un pool de threads où le nombre de threads est égal au nombre de cœurs de processeur disponibles pour la JVM. Le niveau de simultanéité par défaut est un. Cela signifie qu'autant de threads seront créés dans le pool qu'il y a de cœurs de processeur disponibles pour la JVM. Si le niveau de simultanéité est 4, la valeur transmise est utilisée à la place du nombre de cœurs.

ExecutorService executor = Executors.newWorkStealingPool(4);
                    
newSingleThreadExecutor — Crée un pool avec un seul thread pour exécuter toutes les tâches.

ExecutorService executor = Executors.newSingleThreadExecutor();
                    
newCachedThreadPool — Crée un pool de threads qui crée de nouveaux threads selon les besoins, mais réutilise les threads créés précédemment lorsqu'ils sont disponibles.

ExecutorService executor = Executors.newCachedThreadPool();
                    
newScheduledThreadPool — Crée un pool de threads qui peut planifier l'exécution de commandes après un délai donné ou périodiquement.

ScheduledExecutorService executor = Executors.newScheduledThreadPool(10);
                    

Nous examinerons chaque type de piscine dans les leçons suivantes.