Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Impara CompletableFuture | Migliori Pratiche per il Multithreading
Multithreading in Java

bookCompletableFuture

Rimane un ultimo passo! In questo capitolo esamineremo la principale classe di asincronia CompletableFuture.

Utilizziamo un'analogia reale. Prenoti un taxi tramite un'app (creazione del task). Durante l'attesa, l'app può fornire aggiornamenti sulla posizione dell'auto o sull'orario di arrivo stimato (elaborazione del risultato). In caso di problemi come ritardi o cancellazioni, l'app ti notifica e suggerisce alternative (gestione delle eccezioni).

Metodi principali

La prima domanda che potresti porti è come avviare un task utilizzando CompletableFuture. Per farlo, puoi utilizzare il metodo supplyAsync(), progettato per eseguire un task che restituisce il risultato in modo asincrono.

Main.java

Main.java

copy
12345678910
// 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: });

Abbiamo anche il metodo thenAccept(), che gestisce il valore restituito da CompletableFuture nel codice asincrono. Non restituisce nulla; è utile quando è necessario accettare una risposta e processarla in qualche modo.

Note
Nota

Nel nostro esempio, eseguiamo il task asincronamente e future.thenAccept() ottiene la risposta dalla lambda, che possiamo poi utilizzare per stampare sulla console.

Esiste un metodo simile, thenApply(), che funziona come thenAccept() ma ottiene il risultato dell'attività asincrona e restituisce un nuovo risultato.

Main.java

Main.java

copy
12345678910
// 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: });

Cosa succede se non vogliamo ottenere il risultato dell'attività asincrona ma semplicemente essere notificati quando è terminata?

Per questo, si può utilizzare thenRun(), che viene eseguito dopo il completamento dell'attività asincrona.

Main.java

Main.java

copy
12345678
// 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! });

Possiamo anche recuperare il risultato di un'attività asincrona bloccando il thread corrente fino al completamento dell'attività. A questo scopo, utilizziamo il metodo get().

Main.java

Main.java

copy
1234567
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> "Hello World"); try { String result = completableFuture.get(); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); }

Esiste anche un metodo join(), che mette in pausa il thread corrente e attende il completamento dell'attività asincrona. Tuttavia, la differenza tra join() e get() è che lanciano eccezioni diverse.

Main.java

Main.java

copy
12345
// 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();

Qui non gestiamo l'eccezione perché il metodo join() lancia una CompletionException non controllata.

Combinazione e concatenazione dei task

È possibile combinare i task in CompletableFuture utilizzando thenCompose(), che consente di unire due task dipendenti.

Main.java

Main.java

copy
12345678910111213141516171819202122232425
// 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 });

Per prima cosa si recuperano i dati del libro in modo asincrono tramite il metodo getBookDetails(), quindi si utilizza il risultato per eseguire il successivo task asincrono—il recupero dei dati dell'autore tramite il metodo getAuthorDetails(). Dopo il completamento di entrambi i task, il risultato (informazioni sull'autore) viene visualizzato sulla console.

Possiamo anche unire i risultati di due task utilizzando il metodo thenCombine(). Esegue entrambi i task in modo concorrente e combina i loro risultati una volta che entrambi i task sono completati.

Main.java

Main.java

copy
123456789101112
// 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 });

Questo codice recupera in modo asincrono due numeri, li somma e stampa il risultato sulla console.

È anche possibile attendere il completamento di tutti i task utilizzando il metodo allOf(), oppure di uno qualsiasi di essi tramite il metodo anyOf().

Main.java

Main.java

copy
1234567891011121314151617181920
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"); });

Come gestire gli errori e che cos'è un timeout?

Breve estratto dal video

  • handle(): Elabora il risultato o gestisce eventuali eccezioni generate dal CompletableFuture;
  • exceptionally(): Gestisce le eccezioni sollevate durante l'esecuzione del CompletableFuture;
  • completeOnTimeout(): Completa il CompletableFuture con un valore specificato se va in timeout prima del completamento.
Note
Nota

CompletableFuture semplifica la gestione di attività asincrone e l'elaborazione dei loro risultati, rendendolo uno strumento potente per lo sviluppo di applicazioni moderne.

Tutto è chiaro?

Come possiamo migliorarlo?

Grazie per i tuoi commenti!

Sezione 4. Capitolo 6

Chieda ad AI

expand

Chieda ad AI

ChatGPT

Chieda pure quello che desidera o provi una delle domande suggerite per iniziare la nostra conversazione

Awesome!

Completion rate improved to 3.33

bookCompletableFuture

Scorri per mostrare il menu

Rimane un ultimo passo! In questo capitolo esamineremo la principale classe di asincronia CompletableFuture.

Utilizziamo un'analogia reale. Prenoti un taxi tramite un'app (creazione del task). Durante l'attesa, l'app può fornire aggiornamenti sulla posizione dell'auto o sull'orario di arrivo stimato (elaborazione del risultato). In caso di problemi come ritardi o cancellazioni, l'app ti notifica e suggerisce alternative (gestione delle eccezioni).

Metodi principali

La prima domanda che potresti porti è come avviare un task utilizzando CompletableFuture. Per farlo, puoi utilizzare il metodo supplyAsync(), progettato per eseguire un task che restituisce il risultato in modo asincrono.

Main.java

Main.java

copy
12345678910
// 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: });

Abbiamo anche il metodo thenAccept(), che gestisce il valore restituito da CompletableFuture nel codice asincrono. Non restituisce nulla; è utile quando è necessario accettare una risposta e processarla in qualche modo.

Note
Nota

Nel nostro esempio, eseguiamo il task asincronamente e future.thenAccept() ottiene la risposta dalla lambda, che possiamo poi utilizzare per stampare sulla console.

Esiste un metodo simile, thenApply(), che funziona come thenAccept() ma ottiene il risultato dell'attività asincrona e restituisce un nuovo risultato.

Main.java

Main.java

copy
12345678910
// 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: });

Cosa succede se non vogliamo ottenere il risultato dell'attività asincrona ma semplicemente essere notificati quando è terminata?

Per questo, si può utilizzare thenRun(), che viene eseguito dopo il completamento dell'attività asincrona.

Main.java

Main.java

copy
12345678
// 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! });

Possiamo anche recuperare il risultato di un'attività asincrona bloccando il thread corrente fino al completamento dell'attività. A questo scopo, utilizziamo il metodo get().

Main.java

Main.java

copy
1234567
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> "Hello World"); try { String result = completableFuture.get(); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); }

Esiste anche un metodo join(), che mette in pausa il thread corrente e attende il completamento dell'attività asincrona. Tuttavia, la differenza tra join() e get() è che lanciano eccezioni diverse.

Main.java

Main.java

copy
12345
// 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();

Qui non gestiamo l'eccezione perché il metodo join() lancia una CompletionException non controllata.

Combinazione e concatenazione dei task

È possibile combinare i task in CompletableFuture utilizzando thenCompose(), che consente di unire due task dipendenti.

Main.java

Main.java

copy
12345678910111213141516171819202122232425
// 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 });

Per prima cosa si recuperano i dati del libro in modo asincrono tramite il metodo getBookDetails(), quindi si utilizza il risultato per eseguire il successivo task asincrono—il recupero dei dati dell'autore tramite il metodo getAuthorDetails(). Dopo il completamento di entrambi i task, il risultato (informazioni sull'autore) viene visualizzato sulla console.

Possiamo anche unire i risultati di due task utilizzando il metodo thenCombine(). Esegue entrambi i task in modo concorrente e combina i loro risultati una volta che entrambi i task sono completati.

Main.java

Main.java

copy
123456789101112
// 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 });

Questo codice recupera in modo asincrono due numeri, li somma e stampa il risultato sulla console.

È anche possibile attendere il completamento di tutti i task utilizzando il metodo allOf(), oppure di uno qualsiasi di essi tramite il metodo anyOf().

Main.java

Main.java

copy
1234567891011121314151617181920
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"); });

Come gestire gli errori e che cos'è un timeout?

Breve estratto dal video

  • handle(): Elabora il risultato o gestisce eventuali eccezioni generate dal CompletableFuture;
  • exceptionally(): Gestisce le eccezioni sollevate durante l'esecuzione del CompletableFuture;
  • completeOnTimeout(): Completa il CompletableFuture con un valore specificato se va in timeout prima del completamento.
Note
Nota

CompletableFuture semplifica la gestione di attività asincrone e l'elaborazione dei loro risultati, rendendolo uno strumento potente per lo sviluppo di applicazioni moderne.

Tutto è chiaro?

Come possiamo migliorarlo?

Grazie per i tuoi commenti!

Sezione 4. Capitolo 6
some-alt