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 | |
|
| İ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.
GO TO FULL VERSION