Valori cu Domeniu Delimitat (Scoped Values) în Java: Înlocuirea ThreadLocal cu ScopedValue în Java

Ghid complet pentru Scoped Values în Java (JEP 446/481): partajarea sigură și imutabilă a datelor de context în ierarhii masive de Virtual Threads, eliminarea scurgerilor de memorie din ThreadLocal și optimizarea alocărilor.

Sintaxă
ScopedValue.where(CURRENT_USER, user).run(() -> { ... }) | CURRENT_USER.get()

Timp de peste două decenii, transmiterea implicită a contextului de securitate, a ID-urilor de sesiune sau a tranzacțiilor curente s-a bazat pe java.lang.ThreadLocal. Totuși, în arhitectura modernă cu milioane de Virtual Threads, ThreadLocal prezintă defecte arhitecturale majore:

  • Mutabilitate Necontrolată: Orice metodă din lanțul de execuție poate modifica valoarea din ThreadLocal, provocând efecte colaterale greu de urmărit.
  • Scurgeri de Memorie (Memory Leaks): Dacă dezvoltatorul uită să apeleze ThreadLocal.remove(), datele rămân agățate pe fir chiar și după finalizarea cererii.
  • Consum Masiv de Memorie: Moștenirea datelor prin InheritableThreadLocal copiază hărți întregi de referințe pentru fiecare fir copil creat.

Scoped Values (ScopedValue) rezolvă aceste probleme garantând imutabilitate, durată de viață strict delimitată de blocul de execuție și partajare directă fără copiere între fire paralele în concurența structurată.

Definirea și Utilizarea unui ScopedValue
import java.lang.ScopedValue;

record UtilizatorConectat(String id, String rol) {}

public class SecurityContextService {
    // Declarăm ScopedValue ca o constantă publică statică:
    public static final ScopedValue UTILIZATOR_CURENT = ScopedValue.newInstance();

    public void proceseazaCerereHttp(String userId, Runnable handler) {
        var user = new UtilizatorConectat(userId, "ADMIN");

        // Legăm valoarea exclusiv pe durata de execuție a blocului run:
        ScopedValue.where(UTILIZATOR_CURENT, user).run(() -> {
            // În orice metodă apelată adânc în stivă, valoarea este accesibilă:
            executaOperatiuneProtejata();
        });
        // Aici, UTILIZATOR_CURENT nu mai este legat și nu consumă nicio resursă!
    }

    private void executaOperatiuneProtejata() {
        if (UTILIZATOR_CURENT.isBound()) {
            UtilizatorConectat user = UTILIZATOR_CURENT.get();
            System.out.println("Operațiune autorizată pentru: " + user.id() + " cu rol: " + user.rol());
        }
    }
}

Partajarea în Concurența Structurată

Atunci când un task părinte folosește StructuredTaskScope, toate subtask-urile lansate moștenesc automat legăturile ScopedValue active fără nicio alocare suplimentară de memorie, garantând performanță extremă.