Java - Pools de threads

Bonjour à tous, futurs développeurs Java ! Aujourd'hui, nous allons plonger dans le monde passionnant des pools de threads. Ne vous inquiétez pas si vous êtes nouveau dans la programmation ; je vais vous guider à travers ce concept étape par étape, tout comme j'ai fait pour d'innombrables étudiants au fil des années. Alors, prenez un café (ou du thé, si c'est votre préférence), et c'est parti !

Java - Thread Pools

Qu'est-ce qu'un pool de threads ?

Imaginez que vous dirigez un restaurant bondé. Chaque fois qu'un nouveau client arrive, vous pourriez embaucher un nouveau serveur pour le servir. Mais cela serait chaotique et coûteux ! Au lieu de cela, vous avez un nombre fixe de serveurs qui servent plusieurs clients. C'est essentiellement ce qu'un pool de threads fait dans la programmation Java.

Un pool de threads est un groupe de threads préinstanciés et réutilisables qui sont disponibles pour exécuter des tâches. Au lieu de créer un nouveau thread pour chaque tâche, ce qui peut être resource-intensive, nous utilisons un pool de threads existants pour exécuter ces tâches.

Pourquoi utiliser des pools de threads en Java ?

Vous vous demandez peut-être, "Pourquoi s'embêter avec des pools de threads ? Ne pouvons-nous pas simplement créer de nouveaux threads au besoin ?" Eh bien, laissez-moi vous expliquer avec une petite histoire de mon expérience d'enseignement.

Il y a eu un moment où j'avais un étudiant qui essayait de créer un nouveau thread pour chaque tâche dans son application. Son programme fonctionnait bien pour de petites entrées, mais lorsqu'il a essayé de traiter un grand ensemble de données, son ordinateur a presque crashé ! C'est alors que je lui ai présenté les pools de threads.

Voici quelques raisons clés d'utiliser des pools de threads :

  1. Meilleure performance : Créer et détruire des threads coûte cher. Les pools de threads réutilisent les threads, économisant des ressources.
  2. Gestion des ressources : Vous pouvez contrôler le nombre maximum de threads qui peuvent s'exécuter simultanément.
  3. Réactivité améliorée : Les tâches peuvent être exécutées immédiatement par un thread déjà en cours d'exécution.

Création de pools de threads en Java

Java propose plusieurs moyens de créer des pools de threads via la classe Executors. Examinons trois méthodes courantes :

1. Création d'un pool de threads à l'aide de la méthode newFixedThreadPool()

Cette méthode crée un pool de threads avec un nombre fixe de threads. Voici un exemple :

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class FixedThreadPoolExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(5);

for (int i = 0; i < 10; i++) {
Runnable worker = new WorkerThread("" + i);
executor.execute(worker);
}

executor.shutdown();
while (!executor.isTerminated()) {
}
System.out.println("Toutes les threads sont terminées");
}
}

class WorkerThread implements Runnable {
private String message;

public WorkerThread(String s) {
this.message = s;
}

public void run() {
System.out.println(Thread.currentThread().getName() + " (Début) message = " + message);
processMessage();
System.out.println(Thread.currentThread().getName() + " (Fin)");
}

private void processMessage() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

Dans cet exemple, nous créons un pool de threads fixe avec 5 threads. Nous soumettons ensuite 10 tâches à ce pool. Le pool utilisera ses 5 threads pour exécuter ces 10 tâches, en réutilisant les threads dès qu'ils deviennent disponibles.

2. Création d'un pool de threads à l'aide de la méthode newCachedThreadPool()

Cette méthode crée un pool de threads qui crée de nouveaux threads au besoin, mais réutilisera les threads précédemment construits lorsqu'ils seront disponibles. Voyons comment cela fonctionne :

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CachedThreadPoolExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newCachedThreadPool();

for (int i = 0; i < 100; i++) {
executor.execute(new Task());
}

executor.shutdown();
}
}

class Task implements Runnable {
public void run() {
System.out.println("Nom du thread : " + Thread.currentThread().getName());
}
}

Dans cet exemple, nous créons un pool de threads cached et soumettons 100 tâches à ce pool. Le pool créera de nouveaux threads au besoin et les réutilisera si possible.

3. Création d'un pool de threads à l'aide de la méthode newScheduledThreadPool()

Cette méthode crée un pool de threads qui peut planifier des commandes à exécuter après un délai donné, ou à exécuter périodiquement. Voici un exemple :

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ScheduledThreadPoolExample {
public static void main(String[] args) {
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);

Runnable task = () -> System.out.println("Exécution de la tâche à " + System.nanoTime());

executor.scheduleAtFixedRate(task, 0, 2, TimeUnit.SECONDS);
}
}

Dans cet exemple, nous créons un pool de threads planifié avec un thread. Nous planifions ensuite une tâche pour s'exécuter toutes les 2 secondes.

Méthodes de ExecutorService

Voici un tableau des méthodes importantes de l'interface ExecutorService :

Méthode Description
execute(Runnable) Exécute la commande donnée à un moment donné dans le futur
submit(Callable) Soumet une tâche retournant une valeur pour exécution et renvoie un Future
shutdown() Initie un arrêt ordonné dans lequel les tâches précédemment soumises sont exécutées, mais aucune nouvelle tâche ne sera acceptée
shutdownNow() Tente d'arrêter toutes les tâches en cours d'exécution et interrompt le traitement des tâches en attente
isShutdown() Renvoie vrai si cet exécuteur a été arrêté
isTerminated() Renvoie vrai si toutes les tâches ont été terminées après l'arrêt

Conclusion

Et voilà, mes amis ! Nous avons exploré la terre des pools de threads Java, de comprendre pourquoi ils sont utiles à les créer et les utiliser de différentes manières. Rappelez-vous, tout comme dans notre analogie de restaurant, les pools de threads nous aident à gérer nos ressources efficacement.

Au fil des années, j'ai vu des étudiants passer de la lutte avec des concepts de base de threading à la construction d'applications multi-threadées complexes et efficaces. Avec de la pratique et de la patience, vous y arrivingerez aussi !

Continuer votre voyage Java, explorez et expérimentez. Les pools de threads ne sont qu'un outil dans l'arsenal vaste que Java fournit pour la programmation concurrente. Bon codage, et que vos threads soient toujours mis en pool efficacement !

Credits: Image by storyset