Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Apprendre Exécuteurs et Pool de Threads | Mécanismes de Synchronisation de Haut Niveau
Multithreading en Java
course content

Contenu du cours

Multithreading en Java

Multithreading en Java

1. Notions de Base du Multithreading
2. Collections Synchronisées
3. Mécanismes de Synchronisation de Haut Niveau
4. Meilleures Pratiques de Multithreading

book
Exécuteurs et Pool de Threads

Nous avons déjà exploré une variété de mécanismes pour prendre en charge le multithreading, et Executors en fait partie !

Qu'est-ce que les Executors et le Pool de Threads ?

Executors est un mécanisme qui offre des abstractions de haut niveau pour gérer les threads. Il vous permet de créer et de gérer un pool de threads, qui se compose d'un ensemble de threads préexistants prêts à exécuter des tâches. Au lieu de créer un nouveau thread pour chaque tâche, les tâches sont envoyées au pool, où leur exécution est distribuée parmi les threads.

Alors, qu'est-ce qu'un pool de threads exactement ? C'est une collection de threads préexistants qui sont prêts à exécuter des tâches. En utilisant un pool de threads, vous évitez le surcoût de créer et détruire des threads de manière répétée, car les mêmes threads peuvent être réutilisés pour plusieurs tâches.

Remarque

S'il y a plus de tâches que de threads, les tâches attendent dans la Task Queue. Une tâche de la file d'attente est traitée par un thread disponible du pool, et une fois la tâche terminée, le thread prend une nouvelle tâche de la file d'attente. Une fois que toutes les tâches de la file d'attente sont terminées, les threads restent actifs et attendent de nouvelles tâches.

Exemple de la vie

Pensez à un restaurant où les cuisiniers (threads) préparent des commandes (tâches). Au lieu d'embaucher un nouveau cuisinier pour chaque commande, le restaurant emploie un nombre limité de cuisiniers qui gèrent les commandes au fur et à mesure qu'elles arrivent. Une fois qu'un cuisinier a terminé une commande, il prend la suivante, ce qui aide à utiliser efficacement les ressources du restaurant.

Méthode Principale

newFixedThreadPool(int n): Crée un pool avec un nombre fixe de threads égal à n.

java

Main

copy
1
ExecutorService executorService = Executors.newFixedThreadPool(20);

newCachedThreadPool(): Crée un pool qui peut créer de nouveaux threads selon les besoins, mais réutilisera les threads disponibles s'il y en a.

java

Main

copy
1
ExecutorService executorService = Executors.newCachedThreadPool();

newSingleThreadExecutor(): Crée un pool de threads unique qui garantit que les tâches sont exécutées séquentiellement, c'est-à-dire l'une après l'autre. Ceci est utile pour les tâches qui doivent être exécutées dans un ordre strict.

java

Main

copy
1
ExecutorService executorService = Executors.newSingleThreadExecutor();

Dans tous les exemples, les méthodes de Executors renvoient une implémentation de l'interface ExecutorService, qui est utilisée pour gérer les threads.

ExecutorService fournit des méthodes pour gérer un pool de threads. Par exemple, submit(Runnable task) accepte une tâche sous forme d'objet Runnable et la place dans une file d'attente pour exécution. Il renvoie un objet Future, qui peut être utilisé pour vérifier l'état de la tâche et obtenir un résultat si la tâche produit un résultat.

java

Main

copy
12345678910111213141516171819202122232425262728293031323334
package com.example; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class Main { public static void main(String[] args) { // Create a thread pool with 5 threads ExecutorService executor = Executors.newFixedThreadPool(5); // Define the task to be executed Runnable task = () -> { System.out.println("Task is running: " + Thread.currentThread().getName()); try { Thread.sleep(2000); // Simulate some work } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println("Task completed: " + Thread.currentThread().getName()); }; // Submit the task for execution and get a `Future` Future<?> future = executor.submit(task); // Check if the task is done System.out.println("Is task done? " + future.isDone()); // You can use `future` to check the status of the task or wait for its completion // Example: future.get() - blocks until the task is completed (not used in this example) // Initiate an orderly shutdown of the executor service executor.shutdown(); } }

La méthode shutdown() démarre un arrêt en douceur du pool de threads. Elle cesse d'accepter de nouvelles tâches mais terminera les tâches en cours. Une fois que vous appelez cette méthode, le pool ne peut pas être redémarré.

La méthode awaitTermination(long timeout, TimeUnit unit) attend que toutes les tâches du pool soient terminées dans le délai imparti. C'est une attente bloquante qui vous permet de vous assurer que toutes les tâches sont terminées avant de finaliser le pool.

Nous n'avons pas non plus mentionné la principale interface qui aide à suivre l'état du thread, c'est l'interface Future. La méthode submit() de l'interface ExecutorService renvoie une implémentation de l'interface Future.

Si vous souhaitez obtenir le résultat de l'exécution du thread, vous pouvez utiliser la méthode get(), si le thread implémente Runnable, la méthode get() ne renvoie rien, mais si Callable<T>, elle renvoie le type T.

java

Main

copy
12345678910111213141516171819202122232425262728293031
package com.example; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class Main { public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(3); // Callable task that returns a result Callable<String> task = () -> { Thread.sleep(1000); // Simulate some work return "Task result"; }; Future<String> future = executor.submit(task); try { // Get the result of the task String result = future.get(); System.out.println("Task completed with result: " + result); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } executor.shutdown(); } }

Vous pouvez également utiliser la méthode cancel(boolean mayInterruptIfRunning) pour tenter d'annuler l'exécution d'une tâche. Si la tâche n'a pas encore commencé, elle sera annulée. Si la tâche est déjà en cours d'exécution, elle peut être interrompue en fonction du drapeau mayInterruptIfRunning.

true: Si la tâche est en cours d'exécution, elle sera interrompue en appelant Thread.interrupt() sur le thread en cours d'exécution. false: Si la tâche est en cours d'exécution, elle ne sera pas interrompue, et la tentative d'annulation n'aura aucun effet sur la tâche en cours d'exécution.

Bien et 2 méthodes qui comprennent intuitivement ce qu'elles font :

  • isCancelled(): Vérifie si la tâche a été annulée;
  • isDone(): Vérifie si la tâche a été complétée.

Exemple d'utilisation

Il est optimal d'utiliser le nombre de threads = processor cores. Vous pouvez le voir dans le code en utilisant Runtime.getRuntime().availableProcessors().

java

Main

copy
1
int availableProcessors = Runtime.getRuntime().availableProcessors();

Différences entre la création directe de Threads et l'utilisation de ExecutorService

Les principales différences entre la création directe de threads et l'utilisation de ExecutorService sont la commodité et la gestion des ressources. La création manuelle de threads nécessite de gérer chaque thread individuellement, ce qui complique le code et l'administration.

ExecutorService simplifie la gestion en utilisant un pool de threads, facilitant ainsi la gestion des tâches. De plus, alors que la création manuelle de threads peut entraîner une consommation élevée de ressources, ExecutorService vous permet de personnaliser la taille du pool de threads.

Tout était clair ?

Comment pouvons-nous l'améliorer ?

Merci pour vos commentaires !

Section 3. Chapitre 6
We're sorry to hear that something went wrong. What happened?
some-alt