CompletableFuture în Java

Un Future care se poate înlănțui: definești ce se întâmplă cu rezultatul (thenApply, thenAccept) fără să aștepți; join() îl ia la final.

Sintaxă
import java.util.concurrent.CompletableFuture;

CompletableFuture<T> f = CompletableFuture.supplyAsync(() -> valoare);
T join()
T get() throws InterruptedException, ExecutionException
boolean complete(T valoare)
static <T> CompletableFuture<T> completedFuture(T valoare)

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 supplyAsync fără executor rulează pe fire daemon ale ForkJoinPool.commonPool(). Dacă main se 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.