CodeGym /Kurslar /JAVA 25 SELF /Çoxaxınlı proqramların diaqnostikası və sazlanması

Çoxaxınlı proqramların diaqnostikası və sazlanması

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

1. Thread Dump və axınların vəziyyətinin analizi

Thread Dump (axınların dumpu) — bu, tətbiqdəki bütün axınların müəyyən anda vəziyyətinin görüntüsüdür. Bu, bütün axınlarınızın qrup şəkli kimidir: kim nə ilə məşğuldur, kim harada ilişib, kim kimi gözləyir. Thread Dump — deadlock, livelock və digər müəmmalı ilişmələri tapmaq üçün əsas alətinizdir.

Thread Dump-u necə əldə etmək olar?

Terminal vasitəsilə (jstack):

Java prosesinin PID-i varsa, icra edin:

jstack <PID>

Əmr konsolda bütün axınların vəziyyətini, hansının hansı halda olduğunu və hansı monitorları (kilidləri) saxladığını çıxaracaq.

IDE vasitəsilə (IntelliJ IDEA):
Menyudan “Run” → “Show Running List” → prosesi seçin → “Thread Dump”.

VisualVM və ya JConsole vasitəsilə:
Prosesi açın, “Threads” vərəqini tapın və vəziyyətin snapshotunu götürün.

Thread Dump nümunəsi

Dump fraqmenti:

"Thread-1" #12 prio=5 os_prio=0 tid=0x000000001e0c7800 nid=0x1a48 waiting for monitor entry [0x000000001f00f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at com.example.DeadlockDemo.lambda$main$0(DeadlockDemo.java:25)
    - waiting to lock <0x00000000d6d6baf8> (a java.lang.Object)
    - locked <0x00000000d6d6bb08> (a java.lang.Object)

Burada görünür ki, “Thread-1” axını bloklanıb (BLOCKED), bir monitor saxlayır, amma digəri üçün gözləyir. Bir neçə belə axın görürsünüzsə: biri resurs A-nı saxlayır və B-ni gözləyir, digər axın B-ni saxlayır və A-nı gözləyir — bu, klassik deadlock-dur.

Axınların halları

Status Təsvir
RUNNABLE Axın işləyir və ya işləməyə hazırdır
BLOCKED Monitorun (kilidin) ələ keçirilməsini gözləyir
WAITING notify()/notifyAll() çağırışını gözləyir (məsələn, wait() çağırılıb)
TIMED_WAITING Taym-out ilə gözləyir (məsələn, sleep, wait(timeout))
TERMINATED Axın başa çatıb

Vacibdir: RUNNABLE statusu hər zaman axının indi icra olunduğu demək deyil — o, yalnız icraya hazırdır (JVM planlayıcısı onu dərhal işə sala bilməz).

Sizdə deadlock olduğunu necə anlamaq olar?

Dumpda bir neçə axın BLOCKED vəziyyətindədir, hər biri eyni dəstdən olan digər axının saxladığı monitoru gözləyir.

Dumpın sonunda jstack adətən belə yazır:

Found one Java-level deadlock:
=============================
"Thread-1":
  waiting to lock monitor 0x00000000d6d6baf8 (object 0x00000000d6d6baf8, a java.lang.Object),
  which is held by "Thread-2"
"Thread-2":
  waiting to lock monitor 0x00000000d6d6bb08 (object 0x00000000d6d6bb08, a java.lang.Object),
  which is held by "Thread-1"

Əgər axınlar uzun müddət BLOCKED və ya WAITING vəziyyətində qalırsa — bu, araşdırma üçün siqnaldır.

2. Axınların monitorinqi və profillənməsi

VisualVM
VisualVM — pulsuz utilitdir, əksər JDK-lara daxildir. Prosesə qoşulmağa, axınların vəziyyətinə baxmağa, Thread Dump etməyə, CPU-yükünü, aktiv və “asılı qalan” axınları görməyə imkan verir.

Threads vərəqi: neçə axın yaradıldığını, onların halları və aktivlik tarixçəsini görmək olur.

Thread Dump: “Thread Dump” düyməsi jstack-ə bənzər snapshot yaradır.

Java Mission Control və Flight Recorder

Java Mission Control (JMC): JVM-in real vaxt işinin təhlili üçün qabaqcıl alət. Kilidləri, icra vaxtını, allokasiyaları, gecikmələri araşdırmağa kömək edir.

Java Flight Recorder (JFR): JVM-ə inteqrasiya olunmuş profiler, axınlar, kilidlər, pauzalar və s. haqqında hadisələri toplayır.

Nümunə: kilidlərin monitorinqi

VisualVM və ya JMC-də bunu görə bilərsiniz:

  • “A” axını X obyektində bloklanıb.
  • “B” axını X obyektini saxlayır, lakin Y obyektini gözləyir.
  • “C” axını Y obyektini saxlayır, lakin X obyektini gözləyir.

Bu, klassik dövri bloklanmadır (deadlock).

Bu alətlərdən praktikada necə istifadə etməli?

  • Tətbiqi -XX:+FlightRecorder açarı ilə başladın (və ya sadəcə JDK 11+ istifadə edin).
  • JMC-ni açın, prosesə qoşulun, yazımı başladın (start recording).
  • “İsti nöqtələri”, uzunmüddətli kilidlənmələri və axınlar arasındakı rəqabəti analiz edin.

3. Jurnallaşdırma və traslama

Çoxaxınlı proqramlarda “gözlə” sazlama ağrıya səbəb olur. Kritik seksiyalara (synchronized-bloklara) giriş/çıxışı, ümumi dəyişənlərlə əməliyyatları, axınların gözləmə və oyadılmalarını jurnallaşdırın — beləliklə, kimin və nə vaxt resursu ələ keçirdiyini və ya buraxdığını anlayacaqsınız.

Necə jurnallaşdırmalı?

  • Standart vasitələrdən istifadə edin: java.util.logging, SLF4J, Log4j.
  • Axının adını jurnala yazın: Thread.currentThread().getName().
  • Vaxtı və axın identifikatorlarını jurnala yazın.
  • Kilidlərin ələ keçirilməsi/azad edilməsi hadisələrini jurnala yazın.

Jurnallaşdırma nümunəsi

synchronized(lock) {
    System.out.println(Thread.currentThread().getName() + " lock-u ələ keçirdi");
    // kritik bölmə
    System.out.println(Thread.currentThread().getName() + " lock-dan çıxır");
}

Axın adlarının istifadəsi

Axınlara mənalı adlar verin!

Thread t = new Thread(runnable, "MyWorker-1");

Logger ilə traslama nümunəsi

import java.util.logging.Logger;

public class Example {
    private static final Logger logger = Logger.getLogger(Example.class.getName());

    public void doWork() {
        logger.info(Thread.currentThread().getName() + " işə başladı");
        synchronized (this) {
            logger.info(Thread.currentThread().getName() + " synchronized-ə daxil oldu");
            // ...
        }
        logger.info(Thread.currentThread().getName() + " işi bitirdi");
    }
}

4. Diaqnostika üçün ən yaxşı təcrübələr

Kilidlərin əhatəsini minimuma endirin

Kilidləri mümkün qədər az müddət saxlamaq lazımdır.

Pis nümunə:

synchronized(lock) {
    // uzun İ/O
    // mürəkkəb hesablamalar
    // DB-yə müraciət
    // ... və yalnız bundan sonra ümumi verilənlərlə iş
}

Yaxşı nümunə:

// synchronized-dən kənarda: uzun İ/O, hesablamalar

synchronized(lock) {
    // yalnız ümumi verilənlərlə iş
}

Axın adlarından istifadə edin

Mənalı axın adları dump və jurnalların analizinə sərf olunan vaxtı azaldır.

Çoxaxınlılıq üçün testlər yazın

Rəqabətli ssenariləri modelləşdirmək üçün JUnit + CountDownLatch istifadə edin.

CountDownLatch latch = new CountDownLatch(2);
Runnable task = () -> {
    // ...
    latch.countDown();
};
new Thread(task, "Worker-1").start();
new Thread(task, "Worker-2").start();
latch.await(); // hər iki axının bitməsini gözləyirik

ReentrantLock üçün try-finally istifadə edin

Lock lock = new ReentrantLock();
lock.lock();
try {
    // kritik bölmə
} finally {
    lock.unlock();
}

Beləliklə, istisna halında belə kilidi açmağı unutmayacaqsınız. Qarşılıqlı bloklanmadan qaçmaq üçün taym-out ilə tryLock() istifadə edin.

Sinxronizasiyanın niyə lazım olduğunu sənədləşdirin

“Burada synchronized lazımdır, çünki…” kimi şərhlər bir müddət sonra niyyəti anlamağa kömək edəcək.

5. Təcrübə: test proqramında deadlock-un təhlili

Deadlock olan kod nümunəsi

public class DeadlockDemo {
    private static final Object lockA = new Object();
    private static final Object lockB = new Object();

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            synchronized (lockA) {
                System.out.println("Thread-1: lockA-nı ələ keçirdi");
                try { Thread.sleep(100); } catch (InterruptedException ignored) {}
                synchronized (lockB) {
                    System.out.println("Thread-1: lockB-ni ələ keçirdi");
                }
            }
        }, "Thread-1");

        Thread t2 = new Thread(() -> {
            synchronized (lockB) {
                System.out.println("Thread-2: lockB-ni ələ keçirdi");
                try { Thread.sleep(100); } catch (InterruptedException ignored) {}
                synchronized (lockA) {
                    System.out.println("Thread-2: lockA-nı ələ keçirdi");
                }
            }
        }, "Thread-2");

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

Deadlock-u necə tutmaq olar

  1. Proqramı işə salın — o, ilişəcək.
  2. Thread dump əldə edin (jstack ilə və ya VisualVM vasitəsilə).
  3. “Thread-1” və “Thread-2”-ni tapın — hər birinin bir lock-u saxladığını və digərini gözlədiyini görəcəksiniz.
  4. Dumpın sonunda “Found one Java-level deadlock” bölməsi olacaq.

Necə aradan qaldırmalı

  • Kilidləri həmişə eyni ardıcıllıqla ələ keçirin.
  • ReentrantLock-u tryLock() və taym-out ilə istifadə edin: bütün lock-ları ələ keçirmək mümkün olmazsa — buraxın və yenidən cəhd edin.

6. Çoxaxınlı proqramların diaqnostikasında tipik səhvlər

Səhv №1: Thread dump-u oxumağı bacarmamaq. Yeni başlayan tərtibatçılar dumpdan qorxurlar: “Bu nə qəribə stack trace-lər və statuslardır?” Əslində əsas statusları bilmək və BLOCKED/WAITING halları axtarmaq analizi sadələşdirmək üçün kifayətdir.

Səhv №2: Axın adlarını görməməzlikdən gəlmək. Mənalı adlar olmadan dumpda başa düşmək samanlıqda iynə axtarmaq kimidir. Ad verməyə tənbəllik etməyin!

Səhv №3: Həddindən artıq böyük synchronized-bloklar. Böyük kod hissələrini sinxronlaşdırırsınızsa, axınlar daha tez-tez bir-birini bloklayacaq — bunu dumpda tez-tez görünən BLOCKED-lərdən görmək olar.

Səhv №4: RUNNABLE ilə həqiqətən işləyən axını qarışdırmaq. RUNNABLE — hər zaman prosessorda “qaçır” demək deyil. JVM planlayıcısı kimi kimi işə salacağına özü qərar verir.

Səhv №5: Monitorinq alətlərindən istifadə etməmək. Bir çoxları VisualVM, JMC, Flight Recorder haqqında bilmir və println ilə əziyyət çəkir. Alətlərdən istifadə edin — onlar həyatı xeyli asanlaşdırır.

Səhv №6: Kritik əməliyyatların jurnallaşdırılmaması. Jurnallar olmadan kimin nə vaxt kilidi ələ keçirdiyini/buraxdığını anlamaq demək olar ki, mümkünsüzdür.

Səhv №7: Data race-ləri “gözlə” tutmağa çalışmaq. Race-lər həmişə və dərhal özünü göstərmir — CountDownLatch ilə testlərdən istifadə edin, rəqabəti Thread.yield() vasitəsilə gücləndirin və ümumi dəyişənlərin vəziyyətini analiz edin.

1
Tapşırıq
JAVA 25 SELF, səviyyə, dərs
Bağlanıb
Proqram daxilində detektiv: cari axının vəziyyətinin müəyyənləşdirilməsi 🕵️‍♂️
Proqram daxilində detektiv: cari axının vəziyyətinin müəyyənləşdirilməsi 🕵️‍♂️
1
Tapşırıq
JAVA 25 SELF, səviyyə, dərs
Bağlanıb
Gizli Otağa giriş: Kritik bölməyə giriş və çıxışın loglanması 🚪
Gizli Otağa giriş: Kritik bölməyə giriş və çıxışın loglanması 🚪
1
Sorğu/viktorina
, səviyyə, dərs
Əlçatan deyil
Çoxaxınlılıq problemləri
Çoxaxınlılıq problemləri
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION