Kursinhalt
Multithreading in Java
Multithreading in Java
CompletableFuture
Es gibt noch einen letzten Sprung! In diesem Kapitel werden wir uns die Hauptklasse für Asynchronität CompletableFuture
ansehen.
Lassen Sie uns eine realistische Analogie verwenden. Sie rufen ein Taxi über eine App (Aufgabenerstellung). Während Sie warten, kann die App Updates über den Standort des Autos oder die geschätzte Ankunftszeit bereitstellen (Ergebnisverarbeitung). Wenn es Probleme wie Verzögerungen oder Stornierungen gibt, wird die App Sie benachrichtigen und Alternativen vorschlagen (Fehlerbehandlung).
Hauptmethoden
Die erste Frage, die Sie sich stellen könnten, ist, wie man eine Aufgabe starten kann, indem man CompletableFuture
verwendet. Dazu können Sie die supplyAsync()
Methode verwenden, die dazu entwickelt wurde, eine Aufgabe auszuführen, die das Ergebnis asynchron zurückgibt.
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: });
Wir haben auch die thenAccept()
Methode, die den Wert verarbeitet, der von CompletableFuture
in asynchronem Code zurückgegeben wird. Sie gibt nichts zurück; sie ist nützlich, wenn Sie eine Antwort akzeptieren und auf irgendeine Weise verarbeiten müssen.
Hinweis
In unserem Beispiel führen wir die Aufgabe asynchron aus, und
future.thenAccept()
erhält die Antwort von der Lambda, die wir dann verwenden können, um sie auf der Konsole auszudrucken.
Es gibt eine ähnliche Methode, thenApply()
, die wie thenAccept()
funktioniert, aber das Ergebnis der asynchronen Aufgabe erhält und ein neues Ergebnis zurückgibt.
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: });
Was ist, wenn wir das Ergebnis der asynchronen Aufgabe nicht erhalten möchten, sondern einfach nur benachrichtigt werden wollen, wenn sie abgeschlossen ist?
Dafür können wir thenRun()
verwenden, das ausgeführt wird, nachdem die asynchrone Aufgabe abgeschlossen ist.
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! });
Wir können das Ergebnis einer asynchronen Aufgabe auch abrufen, indem wir den aktuellen Thread blockieren, bis die Aufgabe abgeschlossen ist. Zu diesem Zweck verwenden wir die get()
Methode.
Main
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> "Hello World"); try { String result = completableFuture.get(); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); }
Es gibt auch eine Methode join()
, die ebenfalls den aktuellen Thread pausiert und auf die asynchrone Aufgabe wartet, um abgeschlossen zu werden. Der Unterschied zwischen join()
und get()
besteht jedoch darin, dass sie unterschiedliche Ausnahmen werfen.
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();
Wir behandeln die Ausnahme hier nicht, weil die join()
-Methode eine unchecked CompletionException
wirft.
Aufgaben kombinieren und verketten
Wir können Aufgaben kombinieren in CompletableFuture
mit thenCompose()
, das 2 abhängige Aufgaben kombiniert.
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 });
Wir rufen zuerst die Buchdaten asynchron mit der Methode getBookDetails()
ab und verwenden dann das Ergebnis, um die nächste asynchrone Aufgabe auszuführen—das Abrufen der Autorendaten über die Methode getAuthorDetails()
. Nachdem beide Aufgaben abgeschlossen sind, wird das Ergebnis (Autoreninformationen) auf der Konsole angezeigt.
Wir können auch die Ergebnisse von zwei Aufgaben zusammenführen mit der thenCombine()
Methode. Sie führt beide Aufgaben gleichzeitig aus und kombiniert ihre Ergebnisse, sobald beide Aufgaben abgeschlossen sind.
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 });
Dieser Code ruft zwei Zahlen asynchron ab, summiert sie und gibt das Ergebnis auf der Konsole aus.
Wir können auch warten, bis alle Aufgaben abgeschlossen sind, indem wir die allOf()
Methode verwenden, oder auf eine beliebige von ihnen mit der anyOf()
Methode.
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"); });
Wie gehen Sie mit Fehlern um und was ist ein Timeout?
Kurzer Clip aus dem Video
handle()
: Verarbeitet das Ergebnis oder behandelt alle Ausnahmen, die von derCompletableFuture
ausgelöst werden;exceptionally()
: Behandelt Ausnahmen, die während der Ausführung derCompletableFuture
ausgelöst werden;completeOnTimeout()
: Schließt dieCompletableFuture
mit einem angegebenen Wert ab, wenn sie vor dem Abschluss abläuft.
Zusammenfassung
CompletableFuture
erleichtert das Management von asynchronen Aufgaben und die Verarbeitung ihrer Ergebnisse, was es zu einem leistungsstarken Werkzeug für die Entwicklung moderner Anwendungen macht.
Danke für Ihr Feedback!