1. Java-da axının əsas vəziyyətləri
Java-da axın sadəcə “işə salınıb” və ya “dayanıb” deyil. Onun tam bir həyat dövrü var və hər mərhələdə axın fərqli davranır. Bu mərhələləri anlamaq — sabit çoxaxınlı tətbiqlər yazmağın və qəribə “asılmalar” və “gözlənilməz tamamlanmalar”ın sazlanmasının açarıdır.
Hansı vəziyyətlər var?
Java aşağıdakı əsas axın vəziyyətlərini müəyyən edir (onlar Thread.State enum-unda sadalanır):
| Vəziyyət | Təsvir |
|---|---|
|
Axın yaradılıb, lakin hələ işə salınmayıb (new Thread(...), amma start() çağırılmayıb) |
|
Axın icra üçün hazırdır və ya hazırda icra olunur |
|
Axın monitorun azad edilməsini gözləyir (synchronized blokunda bloklanıb) |
|
Axın başqa bir axının onu “oyatmasını” gözləyir (məsələn, Object.wait() vasitəsilə) |
|
Axın taym-out-la gözləyir (məsələn, Thread.sleep(1000), wait(1000), join(1000)) |
|
Axın icrasını tamamlayıb |
Maraqlı fakt:
Köhnə kitab və məqalələrdə başqa adlara və ya bir qədər fərqli sxemlərə rast gələ bilərsiniz. Amma Java 5+ ilə bu vəziyyətlər standart sayılır.
Axının həyat dövrünün vizual sxemi
stateDiagram-v2
[*] --> NEW
NEW --> RUNNABLE: start()
RUNNABLE --> BLOCKED: synchronized-a daxil olma cəhdi, lakin monitor məşğuldur
BLOCKED --> RUNNABLE: monitor azad edildi
RUNNABLE --> WAITING: wait(), join()
RUNNABLE --> TIMED_WAITING: sleep(), wait(timeout), join(timeout)
WAITING --> RUNNABLE: notify()/notifyAll(), join() başa çatdı
TIMED_WAITING --> RUNNABLE: taym-out bitdi / notify()/notifyAll()
RUNNABLE --> TERMINATED: run() başa çatdı
WAITING --> TERMINATED: run() başa çatdı (nadir)
TIMED_WAITING --> TERMINATED: run() başa çatdı (nadir)
2. Axını idarəetmə metodları
Yuxu: Thread.sleep(long millis)
Bəzən axının “yuxuya getməsi” lazımdır ki, digərlərinə mane olmasın və ya hadisəni gözləsin. Thread.sleep(ms) metodu axını müəyyən edilmiş millisekund müddətinə
TIMED_WAITING vəziyyətinə keçirir.
System.out.println("Axın 2 saniyəlik yuxuya gedir...");
Thread.sleep(2000); // 2 saniyəlik yuxuya gedirik
System.out.println("Axın oyandı!");
- Yuxu bitəndən sonra axın
vəziyyətinə qayıdır (işə hazırdır).RUNNABLE - Axın yuxu zamanı dayandırılarsa, InterruptedException atılır.
Başqa axının tamamlanmasını gözləmək: join()
Bəzən axını sadəcə işə salmaq yox, onun işi bitənə qədər gözləmək lazımdır. Bunun üçün Java-da join() metodu var:
Thread t = new Thread(() -> {
System.out.println("İşləyirəm...");
try { Thread.sleep(1000); } catch (InterruptedException e) {}
System.out.println("Hazırdır!");
});
t.start();
System.out.println("t axınının tamamlanmasını gözləyirəm...");
t.join(); // Cari axın (məsələn, main) t-ni gözləyir
System.out.println("t axını tamamlandı!");
Burada əsas axın, join() çağırılan kimi gözləməyə başlayır. Bu müddət ərzində o,
WAITING vəziyyətində olur, ta ki
t axını icranı bitirsin. Taym-outlu variant da var —
join(long millis). Bu halda axın məhdud zaman gözləyir və onun vəziyyəti
TIMED_WAITING olacaq.
Axının dayandırılması: interrupt()
Bəzən istifadəçi “Ləğv et” düyməsini basanda olduğu kimi, axından işi dayandırmasını xahiş etmək lazım olur. Bunun üçün interrupt() metodu istifadə olunur:
Thread t = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
// İşləyirik...
}
System.out.println("Axın dayandırma siqnalına görə tamamlandı!");
});
t.start();
// ... bir müddət sonra:
t.interrupt(); // Axına siqnal: “dayanma vaxtıdır”
Anlamaq vacibdir ki, çağırış interrupt() axını dərhal öldürmür. O, sadəcə xüsusi bir bayraq qurur. Axının özü bu bayrağı vaxtaşırı isInterrupted() vasitəsilə yoxlamalı və öz-özünə tamamlanmalıdır. Əgər axın həmin vaxt yuxu vəziyyətində (sleep()) və ya gözləmədə (wait())dirsə, o, təkcə bayrağı görməyəcək, həm də dərhal InterruptedException alacaq. Məhz belə, Java-da axınların “düzgün” dayandırılması qurulub: proqram onları güc tətbiq edərək dayandırmır, sadəcə tamamlanma vaxtının gəldiyini bildirir.
3. Vəziyyətlər arasında keçid nümunələri
Gəlin nümunələrlə axının öz vəziyyətləri arasında necə “səyahət etdiyinə” baxaq.
Nümunə 1: NEW
→ RUNNABLE
→ TERMINATED
NEWRUNNABLETERMINATEDThread t = new Thread(() -> System.out.println("Salam!"));
System.out.println(t.getState()); // NEW
t.start();
System.out.println(t.getState()); // RUNNABLE (və ya TERMINATED, əgər axın çox sürətlidirsə)
t.join();
System.out.println(t.getState()); // TERMINATED
Nümunə 2: RUNNABLE
→ TIMED_WAITING
→ RUNNABLE
→ TERMINATED
RUNNABLETIMED_WAITINGRUNNABLETERMINATEDThread t = new Thread(() -> {
try {
System.out.println("Yuxuya gedirəm...");
Thread.sleep(1000); // TIMED_WAITING
System.out.println("Oyandım!");
} catch (InterruptedException e) {
System.out.println("Axın dayandırıldı!");
}
});
t.start();
Nümunə 3: RUNNABLE
→ WAITING
join() ilə
RUNNABLEWAITINGThread t1 = new Thread(() -> {
try { Thread.sleep(500); } catch (InterruptedException ignored) {}
System.out.println("t1 tamamlandı");
});
Thread t2 = new Thread(() -> {
try {
t1.join(); // t2, t1-i gözləyir, WAITING vəziyyətindədir
System.out.println("t2, t1-i gözlədi");
} catch (InterruptedException ignored) {}
});
t1.start();
t2.start();
Nümunə 4: BLOCKED
BLOCKEDObject lock = new Object();
Thread t1 = new Thread(() -> {
synchronized (lock) {
try { Thread.sleep(1000); } catch (InterruptedException ignored) {}
System.out.println("t1 synchronized blokundan çıxdı");
}
});
Thread t2 = new Thread(() -> {
synchronized (lock) {
System.out.println("t2 synchronized-a daxil oldu");
}
});
t1.start();
Thread.sleep(100); // t1-ə lock-u ələ keçirməyə imkan verək
t2.start();
Thread.sleep(100); // t2-yə synchronized-a daxil olmağa cəhd etməsi üçün imkan verək
System.out.println("t2-nin vəziyyəti: " + t2.getState()); // BLOCKED
4. Axının vəziyyətini necə bilmək olar? Metodlar isAlive() və getState()
- isAlive() — axın işə salınıb və hələ tamamlanmayıbsa true qaytarır (
artıq false deməkdir).TERMINATED - getState() — axının cari vəziyyətini qaytarır (Thread.State enum-dan dəyər).
Thread t = new Thread(() -> {});
System.out.println(t.isAlive()); // false (NEW)
t.start();
System.out.println(t.isAlive()); // true (RUNNABLE/WAITING/...)
t.join();
System.out.println(t.isAlive()); // false (TERMINATED)
5. Niyə axını birbaşa “öldürmək” olmaz və digər praktiki məsləhətlər
“kill” metodu yoxdur!
Java-da əmrlə axını “öldürməyə” imkan verən metod yoxdur. Niyə? Çünki bu, təhlükəlidir: əgər axın hansısa resursu (fayl, qoşulma, bloklama) saxlayırsa, onu məcburi məhv etmək sistemi uyğunsuz vəziyyətdə qoya bilər.
Köhnəlmiş metodlar: stop(), suspend(), resume()
Java-nın qədim versiyalarında stop(), suspend(), resume() metodları vardı. İndi onlar @Deprecated kimi işarələnib və onların istifadəsi qətiyyən tövsiyə edilmir. Niyə?
- stop() axını istənilən anda öldürə bilər və məlumatı uyğunsuz vəziyyətdə qoya bilər.
- suspend() bloklanmanı saxlayan axını “dondura” bilər və onda bütün proqram asılar.
- resume() bəzən artıq tamamlanmış axını “əridə” bilmir.
Müasir yanaşma:
Axın dayandırma bayrağına (isInterrupted()) və ya digər siqnallara reaksiya verərək özünü düzgün şəkildə tamamlamalıdır.
Ən yaxşı təcrübələr
- Köhnəlmiş kimi işarələnmiş metodları çağırmayın.
- Axını dayandırmaq üçün dayandırma bayrağından istifadə edin (interrupt() və yoxlama isInterrupted()).
- Sazlama zamanı axınların vəziyyətlərini izləyin — bu, asılmaları və deadlock-ları tapmağa kömək edəcək.
- Əgər axının işinin tamamlanmasını gözləmək lazımdırsa, join()-u unutmayın.
6. Axının həyat dövrü ilə işləyərkən tipik səhvlər
Səhv №1: Axını təkrar işə salmaq.
Java-da axını yalnız bir dəfə işə salmaq olar. start() metodunu ikinci dəfə çağırsanız — IllegalThreadStateException alacaqsınız. Tapşırığı təkrar etmək lazımdırsa — yeni bir Thread obyekti yaradın.
Thread t = new Thread(() -> {});
t.start();
t.start(); // İstisna atacaq!
Səhv №2: run() ilə start()-ı qarışdırmaq.
Birbaşa run() çağırmaq kodu yeni axında işə salmır — o, cari axında (məsələn, main) icra olunur. Yalnız start() həqiqətən yeni axını işə salır.
Səhv №3: InterruptedException-i emal etməmək.
Əgər axın yatır və ya gözləyirsə və o dayandırılarsa, InterruptedException atılacaq. Onu görməməzlikdən gəlsəniz, axın “əbədi yuxuya” gedə və ya gözlənilmədən tamamlana bilər.
Səhv №4: Axının vəziyyətini yoxlamamaq.
Bəzən proqram ona görə asılır ki, bir axın artıq tamamlanmış və ya heç vaxt başlamayacaq başqa bir axını gözləyir. Diaqnostika üçün getState() və isAlive() istifadə edin.
Səhv №5: Axını idarə etmək üçün köhnəlmiş metodlardan istifadə etmək.
stop(), suspend(), resume() metodları — zərərlidir. “Hər şeyi tez düzəltmək” çox cazibədar görünsə belə, onlardan istifadə etməyin.
GO TO FULL VERSION