Contenu du cours
Multithreading en Java
Multithreading en Java
CompletableFuture
Il ne reste qu'un dernier saut ! Dans ce chapitre, nous allons examiner la principale classe d'asynchronie CompletableFuture
.
Utilisons une analogie de la vie réelle. Vous hélez un taxi en utilisant une application (création de tâche). Pendant que vous attendez, l'application peut fournir des mises à jour sur la position de la voiture ou le temps d'arrivée estimé (traitement des résultats). S'il y a des problèmes tels que des retards ou des annulations, l'application vous notifiera et proposera des alternatives (gestion des exceptions).
Principales Méthodes
La première question que vous pourriez vous poser est comment démarrer une tâche en utilisant CompletableFuture
. Pour ce faire, vous pouvez utiliser la méthode supplyAsync()
, qui est conçue pour exécuter une tâche qui retourne le résultat de manière asynchrone.
Main
// Creating an asynchronous task CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> { return "Result"; // Result }); // Processing the result after the task completes future.thenAccept(result -> { // Print the result to the console System.out.println("Result received: " + result); // Result received: });
Nous avons également la méthode thenAccept()
, qui gère la valeur retournée par CompletableFuture
dans le code asynchrone. Elle ne retourne rien ; elle est utile lorsque vous devez accepter une réponse et la traiter d'une certaine manière.
Note
Dans notre exemple, nous exécutons la tâche de manière asynchrone, et
future.thenAccept()
obtient la réponse du lambda, que nous pouvons ensuite utiliser pour imprimer sur la console.
Il existe une méthode similaire, thenApply()
, qui fonctionne comme thenAccept()
mais obtient le résultat de la tâche asynchrone et retourne un nouveau résultat.
Main
// Creating an asynchronous task that returns "Hello, World!" CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello, World!"); // Transforming the result to uppercase CompletableFuture<String> transformedFuture = future.thenApply(result -> result.toUpperCase()); // Printing the transformed result to the console transformedFuture.thenAccept(result -> { System.out.println("Transformed result: " + result); // Transformed result: });
Que faire si nous ne voulons pas obtenir le résultat de la tâche asynchrone mais simplement être notifiés lorsqu'elle est terminée?
Pour cela, nous pouvons utiliser thenRun()
, qui est exécuté après que la tâche asynchrone soit terminée.
Main
// Creating an asynchronous task that returns "Hello, World!" CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello, World!"); // Running a task when the previous task completes future.thenRun(() -> { // Print message to the console indicating that the task is complete System.out.println("Task completed!"); // Task completed! });
Nous pouvons également récupérer le résultat d'une tâche asynchrone en bloquant le thread actuel jusqu'à ce que la tâche soit terminée. À cette fin, nous utilisons la méthode get()
.
Main
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> "Hello World"); try { String result = completableFuture.get(); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); }
Il existe également une méthode join()
, qui met également en pause le thread actuel et attend que la tâche asynchrone soit terminée. Cependant, la différence entre join()
et get()
est qu'ils lancent des exceptions différentes.
Main
// Create a `CompletableFuture` that asynchronously executes a task and returns the string "Hello World". CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> "Hello World"); // The `join()` method blocks the current thread until the task is completed and returns the result "Hello World". String result = completableFuture.join();
Nous ne gérons pas l'exception ici car la méthode join()
lance une CompletionException
non vérifiée.
Combinaison des Tâches et Chaînage
Nous pouvons combiner des tâches dans CompletableFuture
en utilisant thenCompose()
qui combinera 2 tâches dépendantes.
Main
// Method to get book details from a remote service public CompletableFuture<String> getBookDetails() { return CompletableFuture.supplyAsync(() -> { // Request to a remote service to get book details return "Book Details"; // Placeholder for book details }); } // Method to get author details from a remote service public CompletableFuture<String> getAuthorDetails(String bookDetails) { return CompletableFuture.supplyAsync(() -> { // Request to another service to get author details return bookDetails + " Author"; // Placeholder for author details }); } // Combine two asynchronous tasks: get book details and then get author details CompletableFuture<String> result = getBookDetails() .thenCompose(book -> getAuthorDetails(book)); // Process the result and print the author details to the console result.thenAccept(author -> { // Print the author details to the console System.out.println("Author: " + author); // Author details });
Nous récupérons d'abord les données du livre asynchrone en utilisant la méthode getBookDetails()
, puis utilisons le résultat pour effectuer la prochaine tâche asynchrone—récupérer les données de l'auteur via la méthode getAuthorDetails()
. Une fois les deux tâches terminées, le résultat (informations sur l'auteur) est affiché sur la console.
Nous pouvons également fusionner les résultats de deux tâches en utilisant la méthode thenCombine()
. Elle exécute les deux tâches simultanément et combine leurs résultats une fois que les deux tâches sont terminées.
Main
// CompletableFuture for adding two numbers CompletableFuture<Double> firstNumberFuture = CompletableFuture.supplyAsync(() -> 50.0); CompletableFuture<Double> secondNumberFuture = CompletableFuture.supplyAsync(() -> 30.0); // Combine the two futures by adding their results CompletableFuture<Double> sumFuture = firstNumberFuture.thenCombine(secondNumberFuture, (first, second) -> first + second); // Print the result of the addition to the console sumFuture.thenAccept(sum -> { System.out.println("Sum: " + sum); // Sum: 80.0 });
Ce code récupère de manière asynchrone deux nombres, les additionne et affiche le résultat dans la console.
Nous pouvons également attendre que toutes les tâches soient terminées en utilisant la méthode allOf()
, ou l'une d'entre elles en utilisant la méthode anyOf()
.
Main
CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> { // Task 1 System.out.println("Task 1 completed"); }); CompletableFuture<Void> future2 = CompletableFuture.runAsync(() -> { // Task 2 System.out.println("Task 2 completed"); }); // Combine both futures and wait for both tasks to complete CompletableFuture<Void> combinedFuture1 = CompletableFuture.allOf(future1, future2); // Combine both futures and proceed as soon as any one task completes CompletableFuture<Object> combinedFuture2 = CompletableFuture.anyOf(future1, future2); // Print a message after all tasks are completed combinedFuture1.thenRun(() -> { System.out.println("All tasks completed"); });
Comment gérez-vous les erreurs et qu'est-ce qu'un délai d'attente ?
Court extrait de la vidéo
handle()
: Traite le résultat ou gère toute exception lancée par leCompletableFuture
;exceptionally()
: Gère les exceptions lancées pendant l'exécution duCompletableFuture
;completeOnTimeout()
: Termine leCompletableFuture
avec une valeur spécifiée s'il expire avant de se terminer.
Résumé
CompletableFuture
facilite la gestion des tâches asynchrones et le traitement de leurs résultats, en faisant un outil puissant pour développer des applications modernes.
Merci pour vos commentaires !