Ce face Thread.ofVirtual
Un fir obișnuit (de platformă) corespunde unui fir al sistemului de operare și consumă destulă memorie, deci poți avea doar câteva mii. Un fir virtual este gestionat de JVM: când așteaptă (rețea, fișiere, sleep), eliberează firul de sistem pe care rula. Astfel poți porni sute de mii de fire virtuale pentru sarcini care stau mult în așteptare, de exemplu cereri către un server.
Thread.startVirtualThread(sarcina) creează și pornește direct un fir virtual. Thread.ofVirtual() returnează un builder cu care poți seta numele (name("cerere-", 0) dă nume numerotate) și apoi crea firul cu start(sarcina) sau unstarted(sarcina). Firele virtuale sunt mereu fire daemon: programul nu le așteaptă la final, așa că folosește join().
Parametri
| Parametru | Descriere |
|---|---|
sarcina |
Codul de rulat (Runnable). |
Ce returnează
ofVirtual: un builder. startVirtualThread: firul pornit.
Exemple
Exemplul 1: Un fir virtual simplu
public class Main {
public static void main(String[] args) throws InterruptedException {
Thread t = Thread.startVirtualThread(() ->
System.out.println("Rulez într-un fir virtual: " + Thread.currentThread().isVirtual()));
t.join();
System.out.println("Firul main este virtual: " + Thread.currentThread().isVirtual());
}
}
Rezultat
Rulez într-un fir virtual: true
Firul main este virtual: false
Exemplul 2: Fire numite cu builderul
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) throws InterruptedException {
String[] orase = {"Iași", "Cluj", "Timișoara"};
String[] rezultate = new String[orase.length];
Thread.Builder builder = Thread.ofVirtual().name("meteo-", 1);
List<Thread> fire = new ArrayList<>();
for (int i = 0; i < orase.length; i++) {
int index = i;
fire.add(builder.start(() ->
rezultate[index] = Thread.currentThread().getName() + ": prognoză pentru " + orase[index]));
}
for (Thread t : fire) {
t.join();
}
for (String r : rezultate) {
System.out.println(r);
}
}
}
Rezultat
meteo-1: prognoză pentru Iași
meteo-2: prognoză pentru Cluj
meteo-3: prognoză pentru Timișoara
Exemplul 3: 10.000 de sarcini care așteaptă
Fiecare sarcină „așteaptă un răspuns” 100 ms. Cu fire virtuale, toate așteaptă în paralel.
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class Main {
public static void main(String[] args) throws InterruptedException {
AtomicInteger terminate = new AtomicInteger();
List<Thread> fire = new ArrayList<>();
for (int i = 0; i < 10_000; i++) {
fire.add(Thread.startVirtualThread(() -> {
try {
Thread.sleep(100);
terminate.incrementAndGet();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}));
}
for (Thread t : fire) {
t.join();
}
System.out.println("Sarcini terminate: " + terminate.get());
}
}
Rezultat
Sarcini terminate: 10000
Greșeli frecvente
- Nu apelezi
join(): firele virtuale sunt daemon, iar programul se poate termina înainte ca ele să lucreze. - Folosești fire virtuale pentru calcule grele pe procesor: nu sunt mai rapide; avantajul lor apare la sarcini care așteaptă mult.
- Faci un pool de fire virtuale: nu e nevoie, sunt ieftine. Creează câte un fir nou pentru fiecare sarcină.
Comenzi înrudite
Fire de execuție virtuale: Firele virtuale sunt fire de execuție foarte ușoare, gestionate de JVM: poți porni zeci de mii de sarcini care așteaptă fără să blochezi sistemul.Thread: Un fir de execuție: cod care rulează în paralel cu restul programului. start() îl pornește, join() așteaptă să se termine.Thread.currentThread: currentThread() dă firul care rulează codul; getName() îi dă numele, iar isAlive() spune dacă firul încă rulează.Thread.sleep: sleep() oprește firul curent pentru un timp; interrupt() trimite unui fir semnalul să se oprească din așteptare.