← Kursa Dön
📄 Text · 12 min

Thread Lifecycle ve Yönetim

Önceki derste thread oluşturmayı ve başlatmayı gördük. Şimdi bir thread'in hayat hikayesini detaylıca inceleyeceğiz. Doğumdan ölüme kadar her aşamayı, thread'leri nasıl yöneteceğimizi ve kontrol edeceğimizi öğreneceğiz.


Thread Yaşam Döngüsü — Detaylı

Bir thread'in hayatını bir insanın iş gününe benzetebilirsin:

  • NEW → Sabah uyanmışsın ama evden çıkmamışsın (oluşturuldu ama başlamadı)

  • RUNNABLE → İşe gitmek için hazırsın, otobüs bekliyorsun (CPU bekliyor)

  • RUNNING → Masanda oturmuş çalışıyorsun (CPU üzerinde çalışıyor)

  • BLOCKED → Toplantı odası dolu, kapıda bekliyorsun (lock bekliyor)

  • WAITING → Patrondan onay bekliyorsun, süre belirsiz (süresiz bekleme)

  • TIMED_WAITING → 15 dk kahve molası verdin (süreli bekleme)

  • TERMINATED → İş günü bitti, eve gittin (thread sonlandı)

        start()
NEW ─────────→ RUNNABLE ←──────→ RUNNING
                  ↑                  │
                  │      ┌───────────┼───────────┐
                  │      ↓           ↓           ↓
                  │   BLOCKED    WAITING    TIMED_WAITING
                  │      │           │           │
                  └──────┘───────────┘───────────┘
                                     │
                                     ↓
                                TERMINATED

Her Durumu Gözlemle

public class ThreadDurumlari {
    public static void main(String[] args) throws Exception {
        Object lock = new Object();
        
        Thread t = new Thread(() -> {
            // RUNNABLE → synchronized bloğuna girmeye çalışır
            synchronized (lock) {
                try {
                    lock.wait(); // WAITING durumuna geçer
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }, "TestThread");
        
        System.out.println("Oluşturuldu: " + t.getState()); // NEW
        
        t.start();
        Thread.sleep(50);
        System.out.println("Başlatıldı: " + t.getState()); // WAITING
        
        synchronized (lock) {
            lock.notify(); // Thread'i uyandır
        }
        
        t.join();
        System.out.println("Bitti: " + t.getState()); // TERMINATED
    }
}

BLOCKED Durumu

Thread, synchronized blok veya metoda girmeye çalışıp monitör kilidi (monitor lock) başka bir thread tarafından tutuluyorsa BLOCKED durumuna geçer.

Object kilit = new Object();

Thread t1 = new Thread(() -> {
    synchronized (kilit) {
        System.out.println("t1 kilidi aldı");
        try {
            Thread.sleep(5000); // 5 saniye kilidi tutar
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}, "t1");

Thread t2 = new Thread(() -> {
    System.out.println("t2 kilidi almaya çalışıyor...");
    synchronized (kilit) { // t1 kilidi bırakana kadar BLOCKED
        System.out.println("t2 kilidi aldı!");
    }
}, "t2");

t1.start();
Thread.sleep(100); // t1'in kilidi almasını bekle
t2.start();

Thread.sleep(100);
System.out.println("t2 durumu: " + t2.getState()); // BLOCKED

WAITING ve TIMED_WAITING

WAITING — Süresiz Bekleme

Object.wait(), Thread.join() veya LockSupport.park() ile girilir.

Thread t = new Thread(() -> {
    synchronized (kilit) {
        try {
            kilit.wait(); // Süresiz bekle — birisi notify edene kadar
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
});

TIMED_WAITING — Süreli Bekleme

Thread.sleep(ms), Object.wait(ms), Thread.join(ms) ile girilir.

Thread t = new Thread(() -> {
    try {
        Thread.sleep(3000); // 3 saniye — TIMED_WAITING
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
});

Thread Priority — Öncelik

Her thread'in bir öncelik değeri var (1-10). Yüksek öncelikli thread'ler daha fazla CPU zamanı alma eğiliminde — ama garanti değil.

Thread t1 = new Thread(() -> {
    for (int i = 0; i < 5; i++) {
        System.out.println("Düşük: " + i);
    }
});

Thread t2 = new Thread(() -> {
    for (int i = 0; i < 5; i++) {
        System.out.println("Yüksek: " + i);
    }
});

t1.setPriority(Thread.MIN_PRIORITY);  // 1
t2.setPriority(Thread.MAX_PRIORITY);  // 10

System.out.println("Varsayılan: " + Thread.NORM_PRIORITY); // 5

t1.start();
t2.start();
SabitDeğer
Thread.MIN_PRIORITY1
Thread.NORM_PRIORITY5 (varsayılan)
Thread.MAX_PRIORITY10

⚠️ Thread priority'ye güvenme! İşletim sistemi ve JVM implementasyonuna göre davranış değişir. Bazı OS'ler priority'yi tamamen görmezden gelir. Kod mantığını priority'ye bağımlı yapma.


Daemon Threads — Arka Plan Thread'leri

İki tür thread var:

  • User thread (normal) — JVM, tüm user thread'ler bitene kadar çalışır

  • Daemon thread — Arka plan servisi, tüm user thread'ler bitince otomatik sonlanır

Daemon thread'ler, temizlik, log yazma, garbage collection gibi arka plan görevleri için kullanılır.

Thread daemon = new Thread(() -> {
    while (true) {
        System.out.println("Daemon çalışıyor...");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            break;
        }
    }
});

daemon.setDaemon(true); // start()'tan ÖNCE ayarlanmalı!
daemon.start();

// Main thread 3 saniye sonra biter
Thread.sleep(3000);
System.out.println("Main bitti");
// Daemon thread otomatik sonlanır — program kapanır
// Daemon mı kontrol et
System.out.println(daemon.isDaemon()); // true

// Main thread her zaman user thread'dir
System.out.println(Thread.currentThread().isDaemon()); // false

💡 Daemon thread kuralları: - setDaemon(true) start()'tan önce çağrılmalı — yoksa IllegalThreadStateException - Daemon thread'den oluşturulan thread'ler de daemon olur - Daemon thread'de kritik iş yapma — JVM kapanınca aniden sonlanır, finally blokları bile çalışmayabilir

Daemon Thread Kullanım Örneği

public class OtomatikKayit {
    
    public static void main(String[] args) throws InterruptedException {
        List<String> loglar = new ArrayList<>();
        
        // Arka planda her 2 saniyede logları "diske yazan" daemon
        Thread logYazici = new Thread(() -> {
            while (true) {
                try {
                    Thread.sleep(2000);
                    if (!loglar.isEmpty()) {
                        System.out.println("[DAEMON] " + loglar.size() + " log yazıldı");
                        loglar.clear();
                    }
                } catch (InterruptedException e) {
                    break;
                }
            }
        }, "LogYazici");
        logYazici.setDaemon(true);
        logYazici.start();
        
        // Ana iş
        for (int i = 0; i < 10; i++) {
            loglar.add("Log #" + i);
            Thread.sleep(500);
        }
        System.out.println("Ana iş bitti");
        // Program kapanır, daemon da sonlanır
    }
}

Thread Interrupt — Thread'i Kesmek

Bir thread'i dışarıdan durdurmak istediğinde interrupt kullanılır. Ama dikkat: interrupt, thread'i zorla durdurmaz — "dur artık" sinyali gönderir. Thread'in bunu kontrol edip kendini durdurması gerekir.

Thread t = new Thread(() -> {
    while (!Thread.currentThread().isInterrupted()) {
        System.out.println("Çalışıyorum...");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            System.out.println("Uyandırıldım, duruyorum.");
            Thread.currentThread().interrupt(); // Bayrağı tekrar set et
            break;
        }
    }
    System.out.println("Thread sonlandı.");
}, "Isci");

t.start();

Thread.sleep(3500);
t.interrupt(); // "Dur" sinyali gönder

Çıktı:

Çalışıyorum...
Çalışıyorum...
Çalışıyorum...
Uyandırıldım, duruyorum.
Thread sonlandı.

Interrupt Mekanizması

İki durum var:

1. Thread bloklanmış durumda ise (sleep, wait, join): InterruptedException fırlatılır ve interrupt bayrağı temizlenir.

2. Thread çalışıyor durumda ise: Sadece interrupt bayrağı set edilir. Thread kontrol etmezse hiçbir şey olmaz.

// Çalışan thread — bayrağı kontrol etmeli
Thread t = new Thread(() -> {
    long toplam = 0;
    for (int i = 0; i < Integer.MAX_VALUE; i++) {
        if (Thread.currentThread().isInterrupted()) {
            System.out.println("Interrupt algılandı, duruyorum.");
            return;
        }
        toplam += i;
    }
});

⚠️ `Thread.stop()` kullanma! Deprecated ve tehlikeli. stop() thread'i aniden öldürür, kilit açılmaz, veri bozulabilir. Her zaman interrupt() + cooperative shutdown kullan.

InterruptedException Yakalama

// YANLIŞ — interrupt'ı yutma!
try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    // Hiçbir şey yapma — KÖTÜ!
}

// DOĞRU — ya yeniden fırlat ya da bayrağı koru
try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    Thread.currentThread().interrupt(); // Bayrağı tekrar set et
    return; // veya break
}

Thread.yield() — CPU'yu Bırak

yield(), mevcut thread'in CPU'yu bırakıp diğer thread'lere şans vermesini önerir. Garanti değil.

Thread.yield(); // "Ben biraz bekleyeyim, başkaları çalışsın"

Pratikte nadiren kullanılır. JVM ve OS zaten thread'leri iyi yönetir.


Thread Bilgileri

Thread t = Thread.currentThread();

System.out.println("İsim: " + t.getName());          // main
System.out.println("ID: " + t.getId());               // 1
System.out.println("Öncelik: " + t.getPriority());    // 5
System.out.println("Durum: " + t.getState());         // RUNNABLE
System.out.println("Daemon: " + t.isDaemon());        // false
System.out.println("Hayatta: " + t.isAlive());        // true
System.out.println("Grup: " + t.getThreadGroup());    // java.lang.ThreadGroup[...]

// Aktif thread sayısı
System.out.println("Aktif thread: " + Thread.activeCount());

Pratik Örnek — Graceful Shutdown

Gerçek uygulamalarda thread'leri düzgün kapatmak önemli:

public class BackgroundService {
    private volatile boolean running = true; // volatile önemli!
    private Thread workerThread;
    
    public void start() {
        workerThread = new Thread(() -> {
            System.out.println("Servis başladı.");
            while (running) {
                try {
                    // İş yap
                    System.out.println("İşlem yapılıyor...");
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
            System.out.println("Servis temiz şekilde kapandı.");
        }, "BackgroundService");
        
        workerThread.start();
    }
    
    public void stop() throws InterruptedException {
        System.out.println("Kapatma sinyali gönderildi...");
        running = false;
        workerThread.interrupt();
        workerThread.join(5000); // Max 5 saniye bekle
        
        if (workerThread.isAlive()) {
            System.out.println("UYARI: Thread hâlâ çalışıyor!");
        }
    }
}
BackgroundService servis = new BackgroundService();
servis.start();

Thread.sleep(5000); // 5 saniye çalışsın
servis.stop();

Çıktı:

Servis başladı.
İşlem yapılıyor...
İşlem yapılıyor...
İşlem yapılıyor...
İşlem yapılıyor...
İşlem yapılıyor...
Kapatma sinyali gönderildi...
Servis temiz şekilde kapandı.

JVM Shutdown Hook

Program kapanırken temizlik yapmak için shutdown hook ekleyebilirsin:

Runtime.getRuntime().addShutdownHook(new Thread(() -> {
    System.out.println("Program kapanıyor, temizlik yapılıyor...");
    // Dosyaları kapat, bağlantıları kes, vs.
}, "ShutdownHook"));

Shutdown hook, JVM kapanırken çalışır: normal çıkış, System.exit(), CTRL+C, hatta kill sinyali. Ama kill -9 veya güç kesintisinde çalışmaz.


Thread Grupları (Kısa Bilgi)

Thread'ler gruplar halinde organize edilebilir:

ThreadGroup grup = new ThreadGroup("İşçiler");

Thread t1 = new Thread(grup, () -> { /* iş */ }, "İşçi-1");
Thread t2 = new Thread(grup, () -> { /* iş */ }, "İşçi-2");

t1.start();
t2.start();

System.out.println("Gruptaki aktif thread: " + grup.activeCount());
grup.list(); // Grup bilgilerini yazdır

Pratikte thread grupları yerine ExecutorService (thread pool) tercih edilir. Bir sonraki konularda göreceğiz.


Özet

  • Thread yaşam döngüsü: NEW → RUNNABLE → RUNNING → BLOCKED/WAITING/TIMED_WAITING → TERMINATED

  • Priority (1-10) thread'e öncelik verir ama davranış garanti değil — koda bağımlı yapma

  • Daemon thread arka plan servisidir, tüm user thread'ler bitince otomatik sonlanır

  • interrupt() thread'e "dur" sinyali gönderir — cooperative shutdown, stop() kullanma

  • volatile flag veya interrupt ile graceful shutdown sağla

  • Thread bilgileri: getName(), getState(), isAlive(), isDaemon() ile sorgulanır