Ce face CompletableFuture
CompletableFuture<T> este un Future mai puternic. Pe lângă get(), îi poți spune ce să facă atunci când rezultatul este gata: să-l transforme (thenApply), să-l folosească (thenAccept), să-l combine cu alt rezultat (thenCombine) sau să trateze o eroare (exceptionally). Fiecare pas returnează un nou CompletableFuture, deci pașii se scriu în lanț.
join() așteaptă rezultatul, ca get(), dar nu aruncă excepții verificate: o eroare din sarcină ajunge ca CompletionException (neverificată). De aceea join() se folosește ușor în lambda și în stream-uri.
Un CompletableFuture poate fi completat și „de mână”: new CompletableFuture<>() creează unul gol, iar complete(valoare) îi dă rezultatul. CompletableFuture.completedFuture(valoare) creează unul deja gata, util în teste.
Parametri
| Parametru | Descriere |
|---|---|
valoare |
Pentru complete / completedFuture: rezultatul dat direct. |
Ce returnează
join() returnează rezultatul sau aruncă CompletionException dacă sarcina a eșuat.
Exemple
Exemplul 1: Un lanț simplu cu join la final
import java.util.concurrent.CompletableFuture;
public class Main {
public static void main(String[] args) {
CompletableFuture<String> mesaj = CompletableFuture
.supplyAsync(() -> 2450)
.thenApply(lei -> lei * 0.19)
.thenApply(tva -> String.format("TVA: %.2f lei", tva));
System.out.println(mesaj.join());
}
}
Rezultat
TVA: 465.50 lei
Exemplul 2: Completare manuală dintr-un alt fir
Firul principal așteaptă în join() până când alt fir apelează complete.
import java.util.concurrent.CompletableFuture;
public class Main {
public static void main(String[] args) throws InterruptedException {
CompletableFuture<String> confirmare = new CompletableFuture<>();
Thread curier = new Thread(() -> {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
return;
}
confirmare.complete("Coletul a ajuns la Ana, în Iași");
});
curier.start();
System.out.println("Gata înainte? " + confirmare.isDone());
System.out.println(confirmare.join());
curier.join();
}
}
Rezultat
Gata înainte? false
Coletul a ajuns la Ana, în Iași
Exemplul 3: join aruncă CompletionException
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
public class Main {
public static void main(String[] args) {
CompletableFuture<Integer> f = CompletableFuture.supplyAsync(() -> Integer.parseInt("12a"));
try {
f.join();
} catch (CompletionException e) {
System.out.println("Eroare: " + e.getCause().getMessage());
}
System.out.println("Gata cu eroare: " + f.isCompletedExceptionally());
System.out.println("completedFuture: " + CompletableFuture.completedFuture(7).join());
}
}
Rezultat
Eroare: For input string: "12a"
Gata cu eroare: true
completedFuture: 7
Greșeli frecvente
- Apelezi
join()după fiecare pas. Pierzi avantajul lanțului; construiește tot lanțul, apoi apeleazăjoin()o singură dată la final. - Uiți că sarcinile
supplyAsyncfără executor rulează pe fire daemon aleForkJoinPool.commonPool(). Dacămainse termină fărăjoin(), programul se oprește înainte ca sarcina să termine.
Comenzi înrudite
CompletableFuture.supplyAsync: Pornește o sarcină pe alt fir și returnează imediat un CompletableFuture; supplyAsync are rezultat, runAsync nu.CompletableFuture.thenApply: thenApply transformă rezultatul când este gata (ca map); thenAccept îl folosește fără să returneze ceva (ca forEach).CompletableFuture.thenCombine: Combină rezultatele a două CompletableFuture independente, după ce sunt gata amândouă.CompletableFuture.allOf: Returnează un CompletableFuture care se termină când s-au terminat toate cele primite; anyOf, când s-a terminat primul.CompletableFuture.exceptionally: Tratează o eroare din lanț: dacă un pas anterior a eșuat, funcția primește excepția și returnează o valoare de rezervă.Future: Rezultatul unei sarcini care rulează pe alt fir: get() așteaptă și îl returnează, isDone() spune dacă s-a terminat.