Introduction à la programmation asynchrone en Java
Les applications modernes nécessitent des performances élevées et une grande réactivité. La programmation asynchrone en Java est une approche qui permet d'exécuter des opérations longues (par exemple, des requêtes de base de données, des appels API, la lecture de fichiers) sans bloquer le thread d'exécution principal. Au lieu d'attendre la fin de la tâche, le programme continue de fonctionner et le résultat est traité dès qu'il est prêt.
Dans ce guide, nous allons aborder les concepts fondamentaux de la programmation asynchrone en Java, en commençant par les outils de base pour finir avec l'API moderne CompletableFuture. Vous apprendrez à créer du code non bloquant, à gérer les erreurs et à combiner des tâches asynchrones.
Concepts fondamentaux : threads et ExecutorService
Threads
La façon la plus simple d'exécuter du code de manière asynchrone en Java est de créer un nouveau thread. Cependant, la création manuelle de threads est une opération coûteuse et, avec un grand nombre de tâches, peut entraîner des problèmes de performance.
// Exemple de création d'un threadThread thread = new Thread(() -> { System.out.println("Tâche asynchrone exécutée dans le thread : " + Thread.currentThread().getName());});thread.start();System.out.println("Le thread principal continue son travail");ExecutorService — gestion d'un pool de threads
Pour une gestion efficace des threads, Java fournit ExecutorService. Il s'agit d'une interface qui permet de créer des pools de threads, de les réutiliser et de contrôler l'exécution des tâches.
import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;
public class ExecutorServiceExample { public static void main(String[] args) { // Création d'un pool de 4 threads ExecutorService executor = Executors.newFixedThreadPool(4); // Envoi d'une tâche pour exécution executor.submit(() -> { System.out.println("Tâche exécutée dans le pool de threads"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Tâche terminée"); }); // Arrêt du pool executor.shutdown(); }}Types principaux de pools de threads :
newFixedThreadPool(n)— nombre fixe de threads.newCachedThreadPool()— nombre dynamique de threads.newSingleThreadExecutor()— un seul thread pour l'exécution séquentielle des tâches.newScheduledThreadPool(n)— pour planifier des tâches avec un délai.
Future et CompletableFuture
Interface Future
Future est une interface qui représente le résultat d'une opération asynchrone. Elle permet de vérifier si la tâche est terminée, d'attendre sa fin et d'obtenir le résultat.
import java.util.concurrent.*;
public class FutureExample { public static void main(String[] args) throws ExecutionException, InterruptedException { ExecutorService executor = Executors.newSingleThreadExecutor(); Future<Integer> future = executor.submit(() -> { Thread.sleep(2000); return 42; }); System.out.println("Attente du résultat..."); Integer result = future.get(); // Bloque le thread jusqu'à l'obtention du résultat System.out.println("Résultat : " + result); executor.shutdown(); }}L'inconvénient de Future est que la méthode get() bloque le thread. Pour une gestion plus flexible des tâches asynchrones, CompletableFuture a été créé.
CompletableFuture — un outil asynchrone puissant
CompletableFuture<