1. Thread sinfi: Java-da ilk axın
Java-da hər icra axını Thread sinfinin obyekti ilə təmsil olunur. Bu sinif — orkestrin dirijoru kimidir: məhz o, axının işə salınmasına, dayandırılmasına və həyat dövrünə nəzarət edir.
Axını işə salmaq üçün:
- Thread sinfinin (və ya ondan miras alan sinfin) obyektini yaratmaq.
- Bu obyekt üçün start() metodunu çağırmaq.
Gəlin bunu praktikada nəzərdən keçirək.
Nümunə 1. Thread-dən miras alma
Axın yaratmağın ən sadə yolu — sinfinizi Thread-dən miras almaq və onun run() metodunu yenidən təyin etməkdir. run() daxilində yazdığınız hər şey ayrı bir axında icra olunacaq.
// Mirasalma ilə sadə axın
public class HelloThread extends Thread {
@Override
public void run() {
System.out.println("Axından salam! Mənim adım: " + getName());
}
}
public class Main {
public static void main(String[] args) {
HelloThread thread = new HelloThread(); // obyekt-axını yaradırıq
thread.start(); // axını işə salırıq
System.out.println("Əsas axın işi tamamlayır.");
}
}
Əslində nə baş verir?
Siz thread.start() çağırdıqda, Java yeni axın yaradır və məhz onun daxilində run() metodunu işə salır. Bu zaman əsas axın (main ilə başlayan) gözləmir və işini paralel olaraq davam etdirir.
Və əsas xəbərdarlıq: start()-ı birbaşa run() çağırışı ilə qarışdırmayın. Əgər thread.run() yazsanız, heç bir yeni axın yaranmayacaq — bu, sadəcə, çağırdığınız həmin axında icra olunan adi metoddur. Həqiqi çoxaxınlılıq yalnız start() ilə başlayır.
getName() nədir?
getName() metodu axının adını qaytarır. Susmaya görə Java axınlara "Thread-0", "Thread-1" və s. kimi adlar verir. Bu, sazlama üçün əlverişlidir.
2. Runnable interfeysi: əksər hallar üçün ən yaxşı üsul
Java — çevikliyi sevən dildir. Thread sinfi artıq Object-dən miras alır və Java-da çoxlu sinif miras alma yoxdur. Sinfinizi Thread-dən miras alsanız, onu başqa bir sinifdən də miras ala bilməyəcəksiniz (məsələn, özünüzün Car sinfiniz varsa və onu axın kimi etmək istəsəniz — seçim etməli olacaqsınız).
Həll: axın məntiqini ayrıca, Runnable interfeysini reallaşdıran obyektə çıxarmaq. Bu, cəmi bir metodlu run() interfeysidir. Sonra Thread sinfinin obyektini yaradırsınız, ona öz Runnable-ınızı ötürürsünüz — və axını işə salırsınız.
Nümunə 2. Runnable ilə sinif
// Runnable-i reallaşdıran sinif
public class HelloRunnable implements Runnable {
@Override
public void run() {
System.out.println("Runnable-dən salam! Axın: " + Thread.currentThread().getName());
}
}
public class Main {
public static void main(String[] args) {
Runnable runnable = new HelloRunnable();
Thread thread = new Thread(runnable); // Runnable-ı Thread-ə ötürürük
thread.start();
System.out.println("Əsas axın işi tamamlayır.");
}
}
Burada biz HelloRunnable sinfini yaradırıq, o, Runnable interfeysini reallaşdırır. main metodunda bu sinfin obyekti yaradılır və Thread konstruktoruna ötürülür. Bundan sonra axın start() çağırışı ilə işə salınır.
Xüsusilə Thread.currentThread() metodunu qeyd etməyə dəyər. Bu, cari axının obyektini almağa və beləliklə, kodumuzun harada icra olunduğunu anlamağa imkan verən statik metoddur.
Niyə bu daha üstün sayılır?
- Sinfiniz istənilən digər sinifdən miras ala bilər (yalnız Thread-dən deyil).
- Eyni Runnable obyektini bir neçə axında işə salmaq üçün yenidən istifadə edə bilərsiniz.
- Kod daha çevik və səliqəli olur.
3. Sintaksis: anonim siniflər və lambda ifadələri
Java yerində saymır! Java 8-dən sonra daha qısa və rahat yaza bilərik.
Nümunə 3. Anonim sinif
Bəzən yalnız bir dəfə lazım olan sinif üçün ayrıca fayl yaratmaq istəmirik. Onu elə istifadə yerində elan etmək olar — buna anonim sinif deyilir.
public class Main {
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Anonim Runnable! Axın: " + Thread.currentThread().getName());
}
});
thread.start();
System.out.println("Əsas axın işi tamamlayır.");
}
}
Nümunə 4. Lambda ifadəsi (Java 8+)
Runnable interfeysi funksionaldır (cəmi bir abstrakt metod var). Buna görə də lambda ifadəsindən istifadə etmək olar:
public class Main {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
System.out.println("Lambda axını! Axın: " + Thread.currentThread().getName());
});
thread.start();
System.out.println("Əsas axın işi tamamlayır.");
}
}
Qısaca:
Runnable “yerində” reallaşdırılır və lambda ifadəsi run() metodunun reallaşmasını təmin edir.
4. Bir neçə axının işə salınması
İstədiyiniz qədər axın işə sala bilərsiniz (demək olar ki — hər şey kompüterin resurslarından asılıdır).
Nümunə 5. Bir neçə axını işə salırıq
public class Main {
public static void main(String[] args) {
for (int i = 1; i <= 3; i++) {
int threadNumber = i; // lambda üçün dəyişənin surətini çıxarmaq vacibdir!
Thread thread = new Thread(() -> {
System.out.println("Axın #" + threadNumber + ": salam!");
});
thread.start();
}
System.out.println("Əsas axın işi tamamlayır.");
}
}
Çıxış fərqli ola bilər!
Axınlar paralel işləyir — hansı mesajın birinci çıxacağını OS və axın planlayıcısı müəyyən edir.
5. Axının həyat dövrü
Axının necə “yaşadığını” anlamaq sazlama və axınlardan düzgün istifadə üçün vacibdir.
Axının əsas vəziyyətləri
- NEW — axın yaradılıb, amma hələ işə salınmayıb (new Thread(...)).
- RUNNABLE — axın işə salınıb (start()), icra oluna bilər.
- TERMINATED — axın icrasını başa vurub (metod run() bitib).
start() və run() çağırıldıqda nə baş verir
- start() — yeni axın yaradır və onun run() metodunu həmin yeni axında çağırır.
- run() — adi metoddur; birbaşa çağırılarsa, cari axında icra olunacaq (yeni axın yaratmayacaq!).
Nümunə:
public class Main {
public static void main(String[] args) {
Thread thread = new Thread(() -> System.out.println("run() axından: " + Thread.currentThread().getName()));
thread.run(); // ƏSAS AXINDA ÇAĞIRILIR!
thread.start(); // AYRI BİR AXINDA ÇAĞIRILIR!
}
}
6. Təcrübə: sadə çoxaxınlı tətbiq yaradırıq
Gəlin tədris tətbiqimizi inkişaf etdirək — güman edək ki, hər axının öz mesajını yazdığı sadə bir jurnallaşdırma sistemi yazırıq.
public class LoggerTask implements Runnable {
private final String message;
public LoggerTask(String message) {
this.message = message;
}
@Override
public void run() {
System.out.println("[" + Thread.currentThread().getName() + "] Jurnal: " + message);
}
}
public class Main {
public static void main(String[] args) {
Thread logger1 = new Thread(new LoggerTask("Sistemin işə salınması"));
Thread logger2 = new Thread(new LoggerTask("Məlumatların yüklənməsi"));
Thread logger3 = new Thread(new LoggerTask("Sorğunun emalı"));
logger1.start();
logger2.start();
logger3.start();
System.out.println("Əsas axın işi tamamlayır.");
}
}
Nə baş verir?
Üç axın mesajlarını konsola paralel şəkildə yazır. Mesajların ardıcıllığı zəmanət verilmir — çoxaxınlılığın mahiyyəti də budur.
7. Faydalı incəliklər
Cədvəl: axın yaratma üsullarının müqayisəsi
| Üsul | Çeviklik | Yenidən istifadə | Tövsiyə olunur? |
|---|---|---|---|
| Thread-dən miras alma | Aşağı | Xeyr | Yalnız sadə/tədris nümunələri üçün |
| Runnable reallaşdırması | Yüksək | Bəli | Bəli |
| Anonim sinif | Orta | Xeyr | Tək dəfəlik tapşırıqlar üçün |
| Lambda ifadəsi | Yüksək | Xeyr | Bəli (Java 8+) |
Sxem: axının işə salınması necə işləyir
+----------------------+
| main (əsas axın) |
+----------------------+
|
v
+----------------------+
| Thread thread = ... |
+----------------------+
|
v
+----------------------+
| thread.start() | ← Axın "oyanır"
+----------------------+
|
v
+----------------------+
| run() yeni axında |
+----------------------+
|
v
+----------------------+
| Axın tamamlandı |
+----------------------+
8. Axınları işə salarkən tipik səhvlər
Səhv №1: run() çağırmaq əvəzinə start().
Yeni başlayanların çox etdiyi səhv — axın obyektində run() metodunu birbaşa çağırmaqdır. Bu halda heç bir yeni axın yaradılmır, metod sadəcə cari (əsas) axında icra olunur. Düzgün yanaşma: axını işə salmaq üçün həmişə start() istifadə edin.
Səhv №2: Axını təkrar işə salmaq.
Eyni Thread obyekti üçün start()-ı bir dəfədən artıq çağırmaq olmaz — bu, IllegalThreadStateException atacaq. Tapşırığı yenidən işə salmaq lazımdırsa, yeni Thread obyekti yaradın.
Səhv №3: Ümumi dəyişənləri sinxronizasiya olmadan dəyişdirmək.
Bir neçə axın eyni dəyişənlərə müraciət edərsə, nəticə gözlənilməz ola bilər (race condition). Sinxronizasiya barədə sonra danışacağıq, hələlik — diqqətli olun.
Səhv №4: Axınların tamamlanmasını nəzərə almamaq.
Axının tamamlanmasını gözləmək vacibdirsə, join() metodundan istifadə edin. Əks halda, əsas axın törəmə axınlardan əvvəl bitə bilər.
Səhv №5: Axının adını sinfin adı ilə qarışdırmaq.
getName() metodu axının adını qaytarır, sinfin adını yox. Sazlama üçün həmişə konstruktorda və ya setName() vasitəsilə axınlara açıq şəkildə ad vermək faydalıdır.
GO TO FULL VERSION