1. Virtual thread-ləri praktikada necə yaratmaq olar
Artıq nəzəriyyədən praktikaya keçməyin vaxtıdır! Klassik şəkildə thread-in necə yaradıldığını artıq bilirsiniz:
Thread t = new Thread(() -> System.out.println("Hello from thread!"));
t.start();
Yaxud bir az daha qısa:
new Thread(() -> System.out.println("Hi!")).start();
İndi Java 21 ilə yeni bir üsulumuz var:
Thread.startVirtualThread(() -> System.out.println("Hello from virtual thread!"));
və ya daha açıq şəkildə:
Thread t = Thread.ofVirtual().start(() -> System.out.println("Hello from virtual thread!"));
Fərq nədədir?
- Thread.ofVirtual().start(...) JVM tərəfindən idarə olunan, OS tərəfindən yox, virtual thread yaradır.
- Thread.ofPlatform().start(...) (və ya new Thread(...)) — klassik thread, əvvəlki kimidir.
Niyə bu vacibdir?
Virtual thread-ləri on minlərlə sayda yaratmaq olar və OutOfMemoryError-dan qorxmağa ehtiyac yoxdur. İndi birdən milyon sorğunu emal etmək istəsəniz — Java belə deyəcək: “Problem deyil, daha da ver!”
2. Virtual thread-in yaradılma sintaksisi
Baza nümunə:
public class VirtualThreadDemo {
public static void main(String[] args) {
Thread thread = Thread.ofVirtual().start(() -> {
System.out.println("Virtual thread-dən salam! Thread: " + Thread.currentThread());
});
// Thread-in tamamlanmasını gözləyirik (main daha tez bitməsin deyə)
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Nə baş verir?
- Biz Thread.ofVirtual().start(...) vasitəsilə virtual thread yaradırıq.
- Thread daxilində sadə əməliyyat var: mesajın çapı.
- Sonda əsas thread-in virtual thread-i gözləməsi üçün thread.join() çağırırıq (əks halda proqram thread nəsə çıxarmadan əvvəl yekunlaşa bilər).
Diqqət:
Virtual thread zahirən və davranışca demək olar ki, adi thread kimidir, amma daxilində — JVM sehridir!
3. Virtual thread-lərin kütləvi yaradılması: Loom-un gücü praktikada
İndi isə adi thread-lərlə riskli (və ya sadəcə mümkünsüz) ola biləcək bir şeyi sınayaq: hər biri öz nömrəsini yazacaq 10_000 virtual thread yaradaq.
public class VirtualThreadMassive {
public static void main(String[] args) throws InterruptedException {
int N = 10_000;
Thread[] threads = new Thread[N];
for (int i = 0; i < N; i++) {
int threadNum = i;
threads[i] = Thread.ofVirtual().start(() -> {
System.out.println("Virtual thread #" + threadNum + " işləyir!");
});
}
// Bütün thread-lərin tamamlanmasını gözləyirik
for (Thread t : threads) {
t.join();
}
System.out.println("Bütün virtual thread-lər tamamlandı!");
}
}
- Adi thread-lər üçün (new Thread(...)) belə kod demək olar ki, proqramınızı OutOfMemoryError ilə “yıxacaq”.
- Virtual thread-lər üçün — bu, normal iş rejimidir! JVM minlərlə və on minlərlə thread-i asanlıqla idarə edəcək.
Yeri gəlmişkən, sizə 10_000 çox görünürsə, 100_000-ə, hətta 1_000_000-a cəhd edin. Müasir maşında JVM öhdəsindən gələcək, əgər thread-ləriniz sadə iş görürsə və ya I/O gözləyirsə.
4. Runnable və lambda ifadələri: kodu virtual thread-ə necə ötürmək olar
Virtual thread-lər tapşırıqları adi thread-lər kimi qəbul edir: Runnable interfeysi vasitəsilə. Bu o deməkdir ki, siz həm lambda ifadələrini, həm metod istinadını, həm də Runnable reallaşdıran istənilən obyekti verə bilərsiniz.
Lambda nümunəsi:
Thread.ofVirtual().start(() -> System.out.println("Virtual thread-də lambda!"));
Metod nümunəsi:
public class TaskRunner {
public static void main(String[] args) {
Thread.ofVirtual().start(TaskRunner::doWork);
}
static void doWork() {
System.out.println("Virtual thread-də işləyirik: " + Thread.currentThread());
}
}
Anonim sinif nümunəsi:
Thread.ofVirtual().start(new Runnable() {
@Override
public void run() {
System.out.println("Virtual thread-də anonim sinif!");
}
});
Nəticə:
Adi thread-lərlə işləyən hər şey virtual thread-lərlə də işləyir — sadəcə indi bu “yüngül və sürətlidir”.
5. ExecutorService ilə müqayisə: köhnə və yeni yanaşma
Klassik ExecutorService
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 100; i++) {
int taskNum = i;
executor.submit(() -> {
System.out.println("Tapşırıq #" + taskNum + " icra olunur");
});
}
executor.shutdown();
Problem:
Tapşırıqlar çox, thread-lər az olanda — tapşırıqlar növbədə gözləyir. Thread-lər həddən artıq çox olanda — proqram resurs çatışmazlığından “boğulur”.
Yeni üsul: virtual thread-lər üzərində Executor
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
for (int i = 0; i < 100_000; i++) {
int taskNum = i;
executor.submit(() -> {
System.out.println("Virtual tapşırıq #" + taskNum);
});
}
executor.shutdown();
Nə baş verir?
- Hər tapşırıq üçün ayrı virtual thread yaradılır.
- JVM onların planlaşdırılmasını özü idarə edir və sistemi yükləmir.
- Pool-un ölçüsünü məhdudlaşdırmağa ehtiyac yoxdur — virtual thread-lər “demək olar ki, pulsuzdur”.
Virtual thread-lər üçün Executor-u nə vaxt istifadə etməli?
- Tapşırıqların böyük axını olduqda və pool-un ölçüsü barədə düşünmək istəmədikdə.
- Tapşırıqlar müstəqildir və paralel icra oluna bilir.
- Sadəlik istədikdə: thread-ləri əl ilə idarə etməyə ehtiyac yoxdur.
6. Praktik məsləhətlər: nə zaman nəyi istifadə etməli
Thread.ofVirtual().start()-ı birbaşa nə vaxt istifadə etməli?
- Tək bir unikal tapşırıq üçün ayrıca thread yaratmaq lazım olduqda (məsələn, test, demonstrasiya və ya sadə eksperiment üçün).
- Thread-lərin sayı az olduqda və onları əl ilə idarə etmək istədikdə.
Executors.newVirtualThreadPerTaskExecutor()-u nə vaxt istifadə etməli?
- Tapşırıqları kütləvi şəkildə işə salmaq lazım olduqda (məsələn, çoxlu sayda sorğuların, faylların, şəbəkə bağlantılarının emalı).
- Tapşırıqlar müstəqildir və bir-biri ilə koordinasiya tələb etmir.
- Virtual thread-ləri artıq ExecutorService istifadə olunan mövcud arxitekturaya inteqrasiya etmək istədikdə (məsələn, veb-serverdə, tapşırıq emalçısında və s.).
Məsləhət:
Əmin deyilsinizsə — virtual thread-lər üçün Executor ilə başlayın. Bu, ən universal və müasir yanaşmadır.
7. Virtual thread-lərdə istisnaların emalı
Virtual thread-lər — try-catch baxımından adi thread-lərdir. Əgər sizin Runnable daxilində istisna baş verərsə, bu bütün JVM-i “sındırmayacaq”, sadəcə həmin thread-i səhvlə tamamlayacaq.
Nümunə:
Thread t = Thread.ofVirtual().start(() -> {
throw new RuntimeException("Nəsə səhv getdi!");
});
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Main thread işi davam etdirdi.");
ExecutorService-də:
Əgər tapşırığı submit vasitəsilə göndərirsinizsə, nəticəni Future ilə əldə edə bilərsiniz və istisna get() çağırıldıqda atılacaq:
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
Future<?> f = executor.submit(() -> {
throw new RuntimeException("Virtual tapşırıqda səhv");
});
try {
f.get();
} catch (ExecutionException e) {
System.out.println("Virtual thread-dən gələn səhvi tutduq: " + e.getCause());
}
executor.shutdown();
8. Virtual thread-ləri yaradarkən tipik səhvlər
Səhv №1: Virtual və platform thread-ləri qarışdırmaq. Əgər siz thread-ləri new Thread(...) və ya Thread.ofPlatform() ilə yaradırsınızsa, bunlar virtual thread deyil. Yalnız Thread.ofVirtual().start(...) və ya Executors-dakı metodlar sizə həqiqi Virtual Threads verir.
Səhv №2: Ağıryüklü hesablamalar üçün sürətlənmə gözləmək. Virtual thread-lər prosessoru güclü yükləyən (CPU-bound) tapşırıqları sürətləndirmir. Əgər milyon thread-in hər biri Pi ədədini milyonuncu rəqəmə qədər hesablayırsa — JVM hesablamaları “sürətləndirə” bilməyəcək, sadəcə thread-lər arasında keçid edəcək.
Səhv №3: Hər thread üçün resursları (məsələn, verilənlər bazası bağlantılarını) saxlamaq. Əgər siz milyon virtual thread yaradırsınızsa, amma hər biri üçün ayrıca verilənlər bazası bağlantısı lazımdırsa — verilənlər bazası dözməyəcək. Virtual thread-lər əsas vaxtı gözləməyə (I/O) gedən, məhdud xarici resurslarla işləməyən tapşırıqlar üçün uyğundur.
Səhv №4: Vacibdirsə thread-lərin tamamlanmasını gözləməmək. Əsas thread virtual thread-lərdən əvvəl bitərsə — proqram nəticələri gözləmədən yekunlaşa bilər. join() və ya shutdown() və awaitTermination() ilə ExecutorService-dən istifadə edin.
Səhv №5: Virtual thread-lərlə uyğun olmayan köhnə kitabxanalardan istifadə etmək. Bəzi üçüncü tərəf kitabxanalar OS səviyyəsində bloklama və ya native sinxronizasiya istifadə edə bilər ki, bu da virtual thread-lərin effektivliyini azaldır. Həmişə uyğunluğu yoxlayın.
GO TO FULL VERSION