CodeGym /Kurslar /JAVA 25 SELF /synchronized, volatile: sintaksis və tətbiq

synchronized, volatile: sintaksis və tətbiq

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

1. Açar söz synchronized: nə üçün və necə

Java-da synchronized açar sözü tualet qapısındakı “Məşğuldur!” lövhəsi kimidir: bir axın “kritik bölmə”nin içində olduğu müddətdə, qalanları növbələrini səbrlə gözləyir. Yalnız birincisi çıxanda, növbəti daxil olub öz kodunu icra edə bilər.

Sintaksis: blok və metod

Sinxronizə olunmuş blok

synchronized (object) {
    // kritik bölmə
}
  • object — kilidi qoymaq istədiyiniz istənilən obyektdir. Bir axın bu bloku icra edərkən, eyni obyektlə bu bloka daxil olmaq istəyən digər axınlar gözləyəcək.

Sinxronizə olunmuş metod

public synchronized void increment() {
    // kritik bölmə
}
  • Burada “kilid” birbaşa obyektin özünə (this) qoyulur. Yəni, eyni anda yalnız bir axın bu obyektin istənilən sinxronizə olunmuş metodunu icra edə bilər.

Statik sinxronizə olunmuş metod

public static synchronized void foo() {
    // kritik bölmə
}
  • Burada bloklama konkret obyektə deyil, sinif səviyyəsində (ClassName.class) baş verir.

Arxa planda bu necə işləyir

Axın sinxronizə olunmuş blok və ya metoda daxil olanda, obyektin “monitor”unu (statik metodlar üçün sinifin monitorunu) ələ keçirir. Əgər monitor artıq məşğuldursa — axın gözləyir. Monitor azad olunan kimi növbəti axın daxil ola bilir.

2. Nümunə: sayğacın artırılması sinxronizasiya ilə və onsuz

Sinxronizasiya olmadan

public class Counter {
    private int count = 0;

    public void increment() {
        count++;
    }

    public int getCount() {
        return count;
    }
}
public class CounterDemo {
    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();

        Runnable task = () -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        };

        Thread t1 = new Thread(task);
        Thread t2 = new Thread(task);

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

        t1.join();
        t2.join();

        System.out.println("Yekun dəyər: " + counter.getCount());
    }
}

Gözlənilən dəyər: 2000
Real dəyər: daha az ola bilər (məsələn, 1995, 1987...), və hər işə salmada — öz “sürprizi”.

Niyə? Çünki count++ əməliyyatı atomar deyil: o, üç hərəkətə bölünür — dəyəri oxumaq, artırmaq, geri yazmaq. İki axın bunu eyni vaxtda edərsə, nəticəni bir-birinin üzərinə “yaza” bilərlər.

Həll: synchronized

public class Counter {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public int getCount() {
        return count;
    }
}

İndi eyni anda yalnız bir axın increment() metodunu icra edə bilər. Yekun dəyər həmişə 2000 olacaq.

Alternativ: sinxronizə olunmuş blok

public class Counter {
    private int count = 0;

    public void increment() {
        synchronized (this) {
            count++;
        }
    }
}

Nəticə eyni olacaq. Bütün metodu deyil, yalnız lazım olan hissəni sinxronizasiya etmək olar.

3. “Obyekt monitoru”na giriş

Monitor — Java-da hər bir obyektə daxili “kiliddir”. Siz synchronized(object) yazanda, axın həmin obyekti “kilidləməyə” çalışır. Kilid boşdursa — axın onu alır, əks halda — növbəsini gözləyir. Axın blokdan çıxan kimi kilid azad olunur.

Vacibdir! Əgər siz müxtəlif obyektlər üzərində sinxronizasiya edirsinizsə — axınlar bir-birini gözləməyəcək. Ona görə də sinxronizasiya üçün düzgün obyekti seçmək çox vacibdir.

Statik sinxronizə olunmuş metodlar

Bəzən ümumi resurs konkret obyekt deyil, sinifin bütün nümunələri üçün ümumi bir şeydir (məsələn, statik dəyişən). Bu halda sinxronizasiya sinif səviyyəsində olmalıdır.

public class StaticCounter {
    private static int count = 0;

    public static synchronized void increment() {
        count++;
    }

    public static int getCount() {
        return count;
    }
}

Bu, buna bərabərdir:

public static void increment() {
    synchronized (StaticCounter.class) {
        count++;
    }
}

Monitor konkret nümunəyə deyil, sinif obyektinə (Class) qoyulur.

4. Açar söz volatile: nədir və niyə lazımdır

Axınlar arasında görünürlük problemi

Java-da hər bir axın işi sürətləndirmək üçün dəyişənlərin dəyərlərini özündə keşləyə bilər. Bu o deməkdir ki, əgər bir axın dəyişəni dəyişdisə, başqa bir axın bunu “fərq etməyə” bilər və dəyəri öz lokal keşindən oxumağa davam edər. Bu, xüsusilə axınların bir-birinə siqnal verdiyi bayraqlar üçün kritikdir.

volatile necə işləyir

Əgər dəyişən volatile kimi elan edilibsə, bu o deməkdir ki:

  • Bütün axınlar onu həmişə əsas yaddaşdan oxuyur və ora yazırlar, keşdən yan keçirlər.
  • Dəyişəndə edilən istənilən dəyişiklik dərhal bütün axınlara görünür.

Amma! volatile ilə əməliyyatlar öz-özlüyündə atomar deyil ( boolean, int və s. kimi sadə primitive-lərin oxunması/yazılması istisna olmaqla). Əgər siz təyinetmədən daha mürəkkəb bir şey edirsinizsə — sinxronizasiya lazımdır.

Nümunə: işin dayandırılması bayrağı

public class Worker extends Thread {
    private volatile boolean running = true;

    public void run() {
        while (running) {
            // nəsə faydalı iş görürük
        }
        System.out.println("Axın tamamlandı");
    }

    public void shutdown() {
        running = false;
    }
}
Worker w = new Worker();
w.start();
// ... müəyyən vaxtdan sonra
w.shutdown();

volatile olmadan axın bayraqdakı dəyişikliyi “fərq etməyə” və sonsuza qədər dövrə düşməyə bilər (xüsusilə çoxnüvəli sistemlərdə). volatile ilə — hər şey lazım olduğu kimi işləyir.

5. volatile məhdudiyyətləri: qeyri-atomarlıq

Bir çox yeni başlayanlar belə düşünür: “Əgər volatile int etsəm, onda count++ yaza və narahat olmaya bilərəm”. Təəssüf ki, belə deyil:

private volatile int count = 0;

public void increment() {
    count++;
}

Səhv! count++ əməliyyatı yenə də atomar deyil — bu, üç addımdır: (1) oxumaq, (2) artırmaq, (3) geri yazmaq. Əgər iki axın eyni anda eyni dəyəri oxuyarsa, hər ikisi onu artıracaq və eyni nəticəni yazacaq — bir inkrement “itiriləcək”.

Nəticə: volatile yalnız dəyişikliklərin görünürlüğünü təmin edir, lakin mürəkkəb əməliyyatlarda yarış şərtlərindən qoruma təmin etmir.

6. synchronized nə zaman, volatile nə zaman istifadə edilməlidir

  • volatile — sadə bir bayraq (məsələn, boolean) olduqda, bir axın yazır, digəri oxuyur. Nümunə: axının dayandırılması, hadisə barədə siqnal.
  • synchronized — mürəkkəb əməliyyatların atomarlığını təmin etmək lazım gəldikdə (məsələn, artırma, bir neçə dəyişənin dəyişdirilməsi, verilənlər strukturları ilə işləmə).

Yadda saxlamaq üçün cədvəl

Ssenari volatile synchronized
Axınlar arasında siqnal ötürmək ✔ ✔
Atomar əməliyyat (artırma) ✖ ✔
Kritik bölmədə çoxsaylı addımlar ✖ ✔
Yalnız dəyişikliklərin görünürlüğü ✔ ✔

synchronized və volatile istifadə edərkən tipik səhvlər

Səhv № 1: Yanlış obyekt üzərində sinxronizasiya. Əgər siz lokal dəyişən və ya hər axında fərqli obyekt üzərində sinxronizasiya edirsinizsə — heç bir qoruma olmayacaq.

Object lock = new Object();
synchronized (lock) {
    // ...
}

Əgər hər axın öz lock-unu yaradırsa — faydası sıfırdır. Bütün axınlar üçün ümumi olan tək sinxronizasiya nöqtəsi, yəni ortaq obyekt lazımdır.

Səhv № 2: volatile-dən atomarlıq gözləmək. volatile görünürlüğü təmin edir, atomarlığı yox. count++ kimi əməliyyatlar sinxronizasiya olmadan hələ də təhlükəsiz deyil.

Səhv № 3: Çox böyük kod sahəsini sinxronizasiya etmək. Əgər siz bütün metodu sinxronizasiya edirsinizsə, halbuki yalnız bir sətri lazımdırsa, digər axınları artıq yerə bloklayır və performansı itirirsiniz. “Kritik bölmə”ni minimallaşdırmağa çalışın.

Səhv № 4: Statik məlumatlar üçün “statik” sinxronizasiyanı unutmaq. Əgər sizdə statik dəyişən varsa və this üzərində sinxronizasiya edirsinizsə, bu kömək etməyəcək. Statik məlumatlar üçün sinif səviyyəsində sinxronizasiya lazımdır: synchronized(ClassName.class).

Səhv № 5: Sətir literalı üzərində sinxronizasiya. Sətirlər üzərində sinxronizasiya təhlükəlidir, çünki eyni literal JVM tərəfindən intern edilir. Proqramın müxtəlif hissələri üçün təsadüfən ortaq bloklamaya səbəb ola bilərsiniz.

1
Tapşırıq
JAVA 25 SELF, səviyyə, dərs
Bağlanıb
İctimai ianələrin uçotu 🌍
İctimai ianələrin uçotu 🌍
1
Tapşırıq
JAVA 25 SELF, səviyyə, dərs
Bağlanıb
Fon təhlükəsizlik xidmətinin idarə edilməsi 🛡️
Fon təhlükəsizlik xidmətinin idarə edilməsi 🛡️
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION