Concurența tradițională în Java (bazată pe ExecutorService și Future) este nestructurată: un task părinte poate lansa mai multe task-uri paralele, dar dacă părintele eșuează sau este anulat, sarcinile copil continuă să ruleze pe fundal ca procese orfane (provocând risipă de CPU, memorie și conexiuni de baze de date lăsate deschise).
Concurența Structurată (Structured Concurrency) tratează un grup de sarcini concurente ca pe o unitate atomică unică de lucru: relațiile părinte-copil sunt strict delimitate sintactic, iar erorile și anulările se propagă automat în cascadă.
import java.util.concurrent.StructuredTaskScope;
import java.util.concurrent.StructuredTaskScope.Subtask;
record Comanda(String utilizator, double total, String adresaLivrare) {}
public class StructuredConcurrencyDemo {
public Comanda preiaDateComanda(long comandaId) throws Exception {
// ShutdownOnFailure: dacă oricare dintre subtask-uri aruncă o excepție,
// celelalte subtask-uri active sunt anulate automat pe loc!
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
// Lansăm două interogări concurente:
Subtask taskUtilizator = scope.fork(() -> interogheazaServiciuUtilizatori(comandaId));
Subtask taskTotal = scope.fork(() -> calculeazaTotalFactura(comandaId));
// Așteptăm ca ambele sarcini să se finalizeze sau ca una să eșueze:
scope.join();
scope.throwIfFailed(); // Propagă excepția primului subtask eșuat!
// Dacă am ajuns aici, ambele sarcini au garantat succes:
return new Comanda(taskUtilizator.get(), taskTotal.get(), "București, Sector 1");
} // La ieșirea din blocul try, toate resursele rămase sunt curățate determinist!
}
private String interogheazaServiciuUtilizatori(long id) { return "Dexter"; }
private double calculeazaTotalFactura(long id) { return 450.75; }
}
Pattern-ul „Primul Răspuns Câștigă” cu ShutdownOnSuccess
Dacă doriți să interogați simultan mai multe surse redundante (de exemplu două noduri dintr-un cluster sau două servere DNS) și să preluați primul rezultat valid:
try (var scope = new StructuredTaskScope.ShutdownOnSuccess()) {
scope.fork(() -> interogheazaServerOglindaA());
scope.fork(() -> interogheazaServerOglindaB());
scope.join();
String rezultatRapid = scope.result(); // Returnează valoarea primului care a terminat; celălalt este anulat!
}