CodeGym /Kurslar /JAVA 25 SELF /Virtual thread-lərin yaradılması: Thread.ofVirtual().star...

Virtual thread-lərin yaradılması: Thread.ofVirtual().start()

JAVA 25 SELF
Səviyyə , Dərs
Mövcuddur

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()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.

Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION