Fire Virtuale în Java (Project Loom): Thread.ofVirtual(), Scalabilitate Masivă și Carrier Threads în Java

Ghid complet pentru Virtual Threads introduse în Java 21 LTS: arhitectura M:N, Carrier Threads, eliminarea blocajelor reactive și migrarea de la Platform Threads la newVirtualThreadPerTaskExecutor.

Sintaxă
Thread.ofVirtual().start(() -> { ... }) | Executors.newVirtualThreadPerTaskExecutor()

Firele Virtuale (Virtual Threads), dezvoltate în cadrul proiectului Project Loom și stabilizate oficial în Java 21 LTS, reprezintă cea mai importantă inovație din istoria concurenței pe platforma Java. Ele readuc simplitatea modelului clasic „un fir per cerere” (thread-per-request), dar cu o scalabilitate apropiată de zero cost.

În mod tradițional, fiecare instanță Thread era un fir de sistem de operare (Platform Thread) cu un consum de 1-2 MB de stivă RAM, limitând un server la câteva mii de conexiuni simultane. Firele virtuale sunt entități gestionate exclusiv de JVM în User Space, au o stivă dinamică de doar câțiva octeți și sunt montate/demontate automat peste un număr mic de fire purtătoare (Carrier Threads).

Rularea a 10.000 de Sarcini Asincrone cu Virtual Threads
import java.time.Duration;
import java.util.concurrent.Executors;
import java.util.stream.IntStream;

public class VirtualThreadsDemo {
    public static void main(String[] args) {
        long start = System.currentTimeMillis();

        // Creăm un executor care alocă un nou Virtual Thread pentru FIECARE sarcină:
        try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
            IntStream.range(0, 10_000).forEach(id -> {
                executor.submit(() -> {
                    // Operația de sleep simulează un apel I/O blocant (ex: interogare SQL sau apel HTTP):
                    // JVM demontează firul virtual de pe nucleul fizic; nucleul rămâne liber pentru alte sarcini!
                    Thread.sleep(Duration.ofMillis(500));
                    return id;
                });
            });
        } // Blocul try-with-resources așteaptă finalizarea tuturor celor 10.000 de sarcini!

        long durata = System.currentTimeMillis() - start;
        System.out.println("10.000 de sarcini finalizate în: " + durata + " ms (consum infim de RAM!)");
    }
}

Capcana Thread Pinning: synchronized vs. ReentrantLock

O atenție deosebită trebuie acordată fenomenului de Thread Pinning: dacă un fir virtual efectuează o operațiune I/O blocantă în interiorul unui bloc synchronized sau al unei metode native C (JNI), JVM nu poate demonta firul virtual de pe firul de OS purtător (Carrier Thread-ul este „țintuit” și blocat).

// DE EVITAT în Java 21 cu Virtual Threads:
synchronized(lock) {
    efectueazaApelReteaBlocant(); // Blochează Carrier Thread-ul fizic!
}

// SOLUȚIA MODERNĂ RECOMANDATĂ:
private final ReentrantLock lock = new ReentrantLock();

lock.lock();
try {
    efectueazaApelReteaBlocant(); // Firul virtual este demontat curat, Carrier Thread-ul rămâne liber!
} finally {
    lock.unlock();
}