La programmation concurrente en Java peut être complexe, mais le package java.util.concurrent
offre des outils puissants pour simplifier cette tâche. Voici quelques exemples concrets pour illustrer comment utiliser ce package dans des situations courantes :
1. Exécuteurs (Executors)
Supposons que vous ayez un ensemble de tâches à exécuter de manière concurrente. Vous pouvez utiliser un exécuteur pour gérer la création et la gestion des threads pour vous. Par exemple :
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
public static void main(String[] args) {
// Crée un pool de threads avec 3 threads
ExecutorService executor = Executors.newFixedThreadPool(3);
// Soumettre des tâches à l'exécuteur
for (int i = 0; i < 10; i++) {
Runnable task = new MonTraitement(i);
executor.execute(task);
}
// Arrête proprement l'exécuteur après avoir terminé toutes les tâches
executor.shutdown();
}
}
class MonTraitement implements Runnable {
private int id;
public MonTraitement(int id) {
this.id = id;
}
public void run() {
System.out.println("Traitement " + id + " en cours sur le thread : " + Thread.currentThread().getName());
}
}
Dans cet exemple, nous utilisons Executors.newFixedThreadPool(3)
pour créer un pool de threads avec 3 threads. Ensuite, nous soumettons 10 tâches à l’exécuteur en utilisant la méthode execute()
. Chaque tâche est une instance de MonTraitement
, qui implémente l’interface Runnable
. L’exécuteur gère la répartition des tâches entre les threads du pool de manière efficace.
2. Verrous (Locks)
Supposons que vous ayez une section critique de code qui doit être exécutée par un seul thread à la fois. Vous pouvez utiliser un verrou pour synchroniser l’accès à cette section critique. Par exemple :
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Main {
private static int compteur = 0;
private static Lock verrou = new ReentrantLock();
public static void incrementer() {
verrou.lock();
try {
compteur++;
} finally {
verrou.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
incrementer();
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
incrementer();
}
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println("Valeur finale du compteur : " + compteur);
}
}
Dans cet exemple, la méthode incrementer()
utilise un verrou pour synchroniser l’incrémentation de la variable compteur
. Seul un thread à la fois peut acquérir le verrou et exécuter la section critique de code. Cela garantit que l’incrémentation se fait de manière sécurisée, même en présence de plusieurs threads.
3. Structures de Données Concurrentes
Supposons que vous ayez besoin d’une collection de données partagée entre plusieurs threads. Vous pouvez utiliser des structures de données concurrentes pour garantir un accès sûr et efficace à ces données. Par exemple :
import java.util.concurrent.ConcurrentHashMap;
public class Main {
public static void main(String[] args) {
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("cle1", 1);
map.put("cle2", 2);
map.put("cle3", 3);
System.out.println("Taille de la carte : " + map.size());
}
}
Dans cet exemple, ConcurrentHashMap
est une structure de données conçue pour permettre un accès concurrent sûr aux éléments de la carte. Les opérations telles que put()
sont atomiques et sécurisées, ce qui garantit l’intégrité des données même en présence de plusieurs threads qui y accèdent simultanément.
Conclusion
Le package java.util.concurrent
offre une multitude d’outils pour simplifier la programmation concurrente en Java. Que ce soit pour la gestion des threads, la synchronisation ou l’accès aux données partagées, ces outils facilitent la création d’applications robustes et performantes. En comprenant comment les utiliser correctement, les développeurs peuvent tirer pleinement parti de la puissance de la programmation concurrente pour répondre aux exigences des applications modernes.