CodeGym /Kurslar /JAVA 25 SELF /Mutekslər və semaforlar: sintaksis və tapşırıqlar

Mutekslər və semaforlar: sintaksis və tapşırıqlar

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

1. Muteks (Mutex): nədir və necə işləyir

Muteks (ingiliscə “mutual exclusion” — “qarşılıqlı istisna”) — yalnız bir axının eyni anda kodun kritik bölməsini icra etməsinə imkan verən mexanizmdir. Əgər muteks məşğuldursa (başqa axın tərəfindən ələ keçirilibsə), digər axınlar onun azad olunmasını gözləyir.

Java-da muteks rolunu çox vaxt kodun üzərində sinxronlaşdığı obyekt oynayır: synchronized. 5-ci versiyasından etibarən Java-da ReentrantLock sinfi ortaya çıxdı — muteksin daha aşkar və çevik reallaşdırması.

Sxematik olaraq

Təsəvvür edin ki, tək açarlı bir otaq var (muteks). Daxil olmaq üçün açarı götürmək lazımdır. Açar yoxdursa (kimsə onu götürüb), qapıda gözləyirsiniz. Açar yerinə qayıdan kimi (muteks azad edilən kimi) növbəti şəxs daxil ola bilər.

Java-da muteksin sintaksisi

Vasitəsilə synchronized (klassik):

public class Counter {
    private int count = 0;

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

Burada bütün increment metodu muteks ilə qorunur — eyni anda onu yalnız bir axın icra edə bilər.

Vasitəsilə ReentrantLock (daha çevik):

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Counter {
    private int count = 0;
    private final Lock lock = new ReentrantLock();

    public void increment() {
        lock.lock(); // Muteksi ələ keçiririk
        try {
            count++;
        } finally {
            lock.unlock(); // Mütləq azad edirik!
        }
    }
}

Vacibdir! Həmişə muteksi finally blokunda azad edin, əks halda “əbədi bloklanma” (deadlock) ilə üzləşə bilərsiniz — və proqram ilişib qalacaq.

Muteks nə zaman lazımdır?

Resursa eyni anda yalnız bir axın yaxınlaşmalı olduqda muteks zəruridir. Bu resurs dəyişən, fayl və ya verilənlər bazası ola bilər. Xüsusilə əməliyyatlar atomik olmadıqda muteksdən istifadə vacibdir: hətta sadə count++ əslində üç addımdan ibarətdir — dəyəri oxumaq, artırmaq və geri yazmaq. Muteks olmadan bir neçə axın bu addımların arasına qarışa və məlumat yarışı yarada bilər.

2. Semafor (Semaphore): nə üçün lazımdır və necə işləyir

Semafor — eyni anda bir neçə axına resursla işləməyə icazə verən, lakin verilmiş həddi aşmayan “tənzimləyici”dir. Limit dolduqda, digər axınlar növbələrini gözləyirlər.

Analogiya: 3 maşınlıq dayanacaq. Bütün yerlər doludursa, gələnlər kimsə getməyənə qədər gözləyirlər.

Java-da semaforun sintaksisi

Bunun üçün java.util.concurrent paketindən Semaphore sinfi istifadə olunur:

import java.util.concurrent.Semaphore;

public class ParkingLot {
    private final Semaphore spots;

    public ParkingLot(int places) {
        this.spots = new Semaphore(places);
    }

    public void parkCar(String car) throws InterruptedException {
        spots.acquire(); // Yeri tutmağa çalışırıq (yoxdursa — gözləyirik)
        try {
            System.out.println(car + " park etdi.");
            Thread.sleep(1000); // Maşın park yerində durur
        } finally {
            spots.release(); // Yeri azad edirik
            System.out.println(car + " getdi.");
        }
    }
}

İstifadə:

ParkingLot parking = new ParkingLot(3);

for (int i = 1; i <= 5; i++) {
    final String car = "Maşın " + i;
    new Thread(() -> {
        try {
            parking.parkCar(car);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }).start();
}

Nəticə: eyni vaxtda dayanacaqda üç maşından çox olmayacaq — qalanlar gözləyir.

Semafor necə işləyir?

  • Semafor yaradılarkən “icazələrin” (permits) sayı təyin olunur.
  • acquire() metodu icazə almağa çalışır: boş varsa — axın keçir, yoxdursa — gözləyir.
  • release() metodu icazəni geri qaytarır.
  • Bir icazəli semafor demək olar ki, muteks kimi davranır, lakin “sahib” olmadan.

3. Muteks və semafor: fərq nədədir?

Xüsusiyyət Muteks (Mutex) Semafor (Semaphore)
Axınların sayı Yalnız bir Bir neçəsi (məhdud sayda)
Tətbiq Resursun qorunması Girişin məhdudlaşdırılması (məsələn, hovuz)
API Java-da
synchronized, Lock
Semaphore
İdarəetmə Adətən “sahib” İstənilən axın azad edə bilər
Tipik ssenari Ümumi sayğac, obyekt Bağlantı hovuzu, dayanacaq, limit

- Muteks — eksklüziv giriş lazım olan hallar üçün.
- Semafor — bir neçəsini buraxmaq olar, amma hamısını yox.

Analogiya: muteks — bir kabinli tualet; semafor — üç kabinli tualet.

4. Praktiki nümunələr

Nümunə 1: Kritik bölmənin qorunması üçün muteks

Tutaq ki, ümumi bank hesabımız var və bir neçə axın hesablar arasında pul köçürür. Əməliyyatlar atomik olmalıdır.

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class BankAccount {
    private int balance;
    private final Lock lock = new ReentrantLock();

    public BankAccount(int initial) {
        this.balance = initial;
    }

    public void deposit(int amount) {
        lock.lock();
        try {
            balance += amount;
        } finally {
            lock.unlock();
        }
    }

    public void withdraw(int amount) {
        lock.lock();
        try {
            if (balance >= amount) {
                balance -= amount;
            }
        } finally {
            lock.unlock();
        }
    }

    public int getBalance() {
        return balance;
    }
}

Burada balansla bağlı istənilən əməliyyatlar muteks ilə qorunur ki, race condition yaranmasın.

Nümunə 2: Girişin məhdudlaşdırılması üçün semafor

Server eyni anda yalnız 2 müştərini emal edə bilər (məsələn, lisenziya səbəbindən).

import java.util.concurrent.Semaphore;

public class Server {
    private final Semaphore connections = new Semaphore(2);

    public void handleRequest(String client) throws InterruptedException {
        connections.acquire();
        try {
            System.out.println(client + " serverə qoşuldu.");
            Thread.sleep(2000); // Sorğunun emulyasiyası
        } finally {
            connections.release();
            System.out.println(client + " ayrıldı.");
        }
    }
}

İstifadə:

Server server = new Server();

for (int i = 1; i <= 5; i++) {
    final String client = "Müştəri " + i;
    new Thread(() -> {
        try {
            server.handleRequest(client);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }).start();
}

Nəticə: server eyni anda iki müştəridən çoxuna xidmət göstərmir.

5. İstifadə xüsusiyyətləri və incəliklər

Muteks: həmişə azad et!
İstisnalar olduqda belə unlock() çağırmağı (və ya sinxronlaşdırılmış blokdan çıxmağı) unutmaq olmaz. try-finally istifadə edin:

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

Unutsanız — “əbədi bloklanma” yarana bilər; digər axınlar sonsuzadək gözləyəcək.

Semafor: başqasının axınını azad etmək olarmı?
Mutekslərdən fərqli olaraq, release() istənilən axın tərəfindən çağırıla bilər, hətta acquire() etməyən tərəfindən də. Bu bəzən rahatdır, amma səhv etmək asandır — nizam-intizamı qoruyun.

Bir icazəli Semaphore = muteks?
Demək olar ki. Lakin semaforda “sahib” anlayışı yoxdur: istənilən azadetmə icazə sayğacını artırır. Muteksdə isə azad etməli olan onu ələ keçirəndir.

Semaforla hovuzu qarışdırmayın
Semafor obyektlər hovuzu deyil, sadəcə “icazə sayğacı”dır. Onu tez-tez hovuzların (məsələn, DB-ə qoşulma hovuzu) reallaşdırılmasında istifadə edirlər, amma özü heç nə saxlamır.

6. Mutekslər və semaforlarla işləyərkən tipik səhvlər

Səhv №1: unlock/release çağırmağı unutmaq. Əgər muteksi və ya semaforu ələ keçirdinizsə, amma unlock() və ya release() çağırmadınızsa, digər axınlar sonsuza qədər ilişib qala bilər. Həmişə try-finally istifadə edin ki, istisnalar zamanı belə kilidin azad edilməsinə zəmanət verilsin.

Səhv №2: Yanlış obyekt üzərində sinxronizasiya. Əgər bütün axınlar üçün ümumi olmayan dəyişən üzərində sinxronlaşsanız (məsələn, lokal dəyişən və ya sətir literalında), sinxronizasiya işləməyəcək.

Səhv №3: İkiqat azadetmə. Semafor halında: acquire()-dan çox dəfə release() çağırılarsa, icazələrin sayı limitdən artıq artacaq. Balansa nəzarət edin!

Səhv №4: Muteks əvəzinə semafor (və ya əksinə) istifadə etmək. Eksklüziv giriş lazımdırsa, muteksdən istifadə edin (synchronized və ya Lock). Eyni anda işləyən axınların sayını məhdudlaşdırmaq lazımdırsa — Semaphore istifadə edin.

Səhv №5: Bloklamanı uzun müddət saxlamaq. Axın muteksi və ya semaforu nə qədər uzun saxlayırsa, digərləri bir o qədər çox gözləyir. Kritik bölmədə iş vaxtını minimuma endirin.

Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION