Concurrence et multithreading en Java : des bases aux techniques avancées
Les applications modernes exigent des performances maximales. En Java, la concurrence et le multithreading sont des outils clés pour une utilisation efficace des processeurs multicœurs. Dans cet article, nous allons voir comment travailler correctement avec les threads, la synchronisation et les utilitaires de concurrence modernes.
Qu'est-ce que le multithreading en Java ?
Le multithreading est la capacité d'un programme à exécuter plusieurs tâches simultanément. En Java, chaque thread est un processus léger possédant sa propre pile d'appels. Principaux avantages :
- Amélioration des performances grâce à l'exécution parallèle
- Interface utilisateur plus réactive
- Utilisation efficace des ressources CPU
Création de threads : deux approches
En Java, il existe deux façons principales de créer un thread :
1. Héritage de la classe Thread
public class MyThread extends Thread { @Override public void run() { System.out.println("Thread en cours d'exécution : " + Thread.currentThread().getName()); }
public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); // Démarrage du thread }}2. Implémentation de l'interface Runnable
public class Task implements Runnable { @Override public void run() { System.out.println("Tâche exécutée dans le thread : " + Thread.currentThread().getName()); }
public static void main(String[] args) { Thread thread = new Thread(new Task()); thread.start(); }}Il est recommandé d'utiliser Runnable, car il ne limite pas la possibilité d'hériter d'autres classes.
Synchronisation des threads : éviter les race conditions
Lorsque plusieurs threads accèdent à des données partagées, des problèmes de synchronisation surviennent. Prenons l'exemple classique d'un compteur :
public class Counter { private int count = 0;
public synchronized void increment() { count++; }
public synchronized int getCount() { return count; }}Le mot-clé synchronized garantit qu'un seul thread peut exécuter la méthode à la fois. Une alternative est l'utilisation de Lock du package java.util.concurrent.locks.
Deadlock : comment éviter un blocage mutuel
Un deadlock se produit lorsque deux threads attendent la libération des ressources de l'autre. Exemple :
public class DeadlockExample { private final Object lock1 = new Object(); private final Object lock2 = new Object();
public void method1() { synchronized (lock1) { synchronized (lock2) { // Section critique } } }
public void method2() { synchronized (lock2) { synchronized (lock1) { // Section critique } } }}Pour éviter un deadlock, verrouillez toujours dans le même ordre ou utilisez tryLock() avec un timeout.
ExecutorService : gestion d'un pool de threads
Créer des threads manuellement est inefficace. Java fournit ExecutorService pour gérer un pool de threads :
import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;
public class ThreadPoolExample { public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(4);
for (int i = 0; i < 10; i++) { executor.submit(() -> { System.out.println("Exécuté dans : " + Thread.currentThread().getName()); }); }
executor.shutdown(); // Arrêt après l'exécution de toutes les tâches }}Les principaux