Mobil tətbiqlərdə Mutex — bu nədir, iş prinsipi və qarşılıqlı eksklüzivliyin tətbiqi

Müəllif: IT Sectr Dərc olunub: 2026-03-18 Oxuma vaxtı: 10 dəq

Mutex (qarşılıqlı eksklüzivlik) — sinxronizasiya primitividir ki, yalnız bir thread-in kritik kod bölməsini hər an icra edə bilməsini təmin edir. Microsoft Docs (Synchronization Objects, 2024)-a görə, Mutex-in əsas prinsipi sahiblikdir: Mutex-i ələ keçirən thread onun sahibi olur və onu yalnız kritik bölmədən çıxdıqda azad edir. Mutex — Race Condition-un qarşısını almaq və çoxthreadli tətbiqlərdə məlumat bütövlüyünü təmin etmək üçün fundamental vasitədir.

Əsas məqamlar

  • Mutex — resursa eyni anda yalnız bir thread-in girişini təmin edən qarşılıqlı eksklüzivlik mexanizmi
  • Sahiblik (ownership) — Mutex-in əsas xüsusiyyəti: kilidi yalnız onu ələ keçirən thread azad edə bilər
  • Semaforadan fərqli olaraq ≥2 sayğacı ilə, Mutex yalnız 0 və ya 1 vəziyyətinə malikdir (ikili semafor)
  • Mutex ilə Deadlock bir neçə muteksin səhv ələ keçirmə ardıcıllığında yaranır
  • Kotlin Coroutines-də suspending Mutex ƏS thread-ni bloklamır, bu da onu klassik ReentrantLock-dan fərqləndirir

Mutex nədir?

Mutex (Mutual Exclusion — qarşılıqlı eksklüzivlik sözünün qısaltması) — çoxthreadli mühitdə ortaq resursa girişi idarə edən sinxronizasiya obyektidir. Thread kritik bölməyə daxil olduqda Mutex-i ələ keçirir. Başqa bir thread eyni Mutex-i ələ keçirməyə çalışarsa, birinci thread kilidi azad edənə qədər gözləmə vəziyyətinə keçir.

Mutex-in arxitekturası 1965-ci ildə Edsger Dijkstra tərəfindən hazırlanmış THE əməliyyat sisteminə gedib çıxır. Məhz Dijkstra semafor konsepsiyasını təqdim etdi, onlardan sonra Mutex xüsusi hal kimi ayrıldı — sahiblik dəstəyi ilə ikili semafor. Müasir ƏS-lər (Linux, Windows, Android) Mutex-i nüvə səviyyəsində tətbiq edir ki, bu da müxtəlif proseslər arasında sinxronizasiyanın düzgün işləməsini təmin edir.

Mutex-in əsas xüsusiyyəti ownership (sahiblik)-dir. Yalnız muteksi ələ keçirən thread onu azad edə bilər. Bu, Mutex-i ikili semaforadan fərqləndirir, burada istənilən thread siqnal (V-əməliyyatı) icra edə bilər. Sahiblik kilidin başqa bir thread tərəfindən təsadüfən azad edilməsinin qarşısını alır ki, bu da Mutex-i mobil inkişafda tipik sinxronizasiya ssenariləri üçün daha təhlükəsiz edir. Android Developer Docs (Processes and Threads, 2024)-ə görə, yüksək rəqabətdə Mutex-in synchronized əvəzinə istifadəsi məhsuldarlığı 30% artıra bilər.

Mutex necə işləyir

Vəziyyətlər və əməliyyatlar

Mutex iki vəziyyətdən birində olur: kidlənmiş (locked) — thread tərəfindən ələ keçirilmiş; sərbəst (unlocked) — ələ keçirilməmiş. İki əsas əməliyyat — lock() (ələ keçirmə) və unlock() (azad etmə). Əgər Mutex artıq ələ keçirilibsə, lock() çağıran thread azad olana qədər bloklanır. JVM-də bloklanmış thread BLOCKED vəziyyətinə keçir və CPU sərf etmir.

Gözləyən thread-lərin planlaşdırılması

Mutex azad edildikdə, sistem gözləyən thread-lərdən hansının kilidi alacağını seçir. Qeyri-ədalətli (non-fair) planlaşdırmada seçim muteksi yenicə azad etmiş thread-ə düşə bilər — bu ötürmə qabiliyyətini artırır, lakin Starvation (aclıq) yarada bilər. Ədalətli (fair) planlaşdırıcı FIFO növbəsindən istifadə edir: ilk gözləyən thread kilidi birinci alır. ReentrantLock(true) məhz bu mexanizmi tətbiq edir.

Rekursiv ələ keçirmə (Reentrancy)

Java/Kotlin-də Mutex tətbiqlərinin əksəriyyəti rekursiv (reentrant) ələ keçirməni dəstəkləyir. Əgər thread artıq Mutex-ə sahibdirsə və yenidən lock() çağırırsa, əməliyyat uğurlu olur — Mutex özünü bloklamır. Rekursiya sayğacı artır və thread unlock()-u lock() qədər çağırmalıdır. Bu, rekursiv çağırışlar və iç-içə kritik bölmələr üçün vacibdir.

Kotlin kodunda Mutex istifadə nümunəsi

Tipik bir tapşırığı nəzərdən keçirək — ortaq sayğacın ReentrantLock (Java/Kotlin-də klassik Mutex) ilə Race Condition-dan qorunması. Mutex olmadan kod səhv nəticə verərdi; Mutex ilə bütün 1000 thread sayğacın dəyərini garantili şəkildə artırır.

kotlin
import java.util.concurrent.locks.ReentrantLock

class MutexCounter {
    private val mutex = ReentrantLock()
    private var count = 0

    fun increment() {
        mutex.lock()
        try {
            count++  // kritik bölmə
        } finally {
            mutex.unlock()  // məcburi finally
        }
    }

    fun getCount(): Int {
        mutex.lock()
        try {
            return count
        } finally {
            mutex.unlock()
        }
    }
}

fun main() = runBlocking {
    val counter = MutexCounter()
    val jobs = List(1000) {
        launch(Dispatchers.Default) {
            counter.increment()
        }
    }
    jobs.forEach { it.join() }
    println(counter.getCount())  // Həmişə 1000
}

Finally blokuna diqqət yetirin — Mutex ilə işləyərkən məcburi nümunədir. Əgər kritik bölmə daxilində istisna yaranarsa, unlock() çağırılmayacaq və Mutex əbədi olaraq bloklanmış qalacaq — bu Deadlock-a gətirib çıxarır. Finally bloku bölmənin icrasının istənilən nəticəsində Mutex-in azad edilməsini təmin edir.

Kotlin-də alternativ yanaşma — withLock genişləndirmə funksiyasından istifadə, avtomatik olaraq lock/unlock-u finally ilə idarə edir.

kotlin
fun increment() {
    mutex.withLock {  // lock + try/finally avtomatik
        count++
    }
}

fun getCount(): Int = mutex.withLock { count }

Mutex vs Semaphore vs Monitor

Bu üç sinxronizasiya mexanizmi tez-tez qarışdırılır, baxmayaraq ki, müxtəlif xüsusiyyətlərə və tətbiq sahələrinə malikdirlər. Mutex — ikili, sahibliklə. Semaphore — icazə sayğacı, sahibliksiz. Monitor — yüksək səviyyəli mexanizm, Mutex-i şərt dəyişənləri (condition variables) ilə birləşdirir. Fərqləri başa düşmək konkret tapşırıq üçün düzgün alətin seçilməsi üçün kritik əhəmiyyət daşıyır.

ParametrMutexSemaphoreMonitor
Tipİkili (0/1)Sayğaclı (0..N)İkili + şərtlər
SahiblikYalnız sahib unlock edə bilərİstənilən thread signal edə bilərYalnız sahib
RekursivlikAdətən bəli (reentrant)XeyrBəli
Şərti gözləməXeyr (Condition lazımdır)XeyrDaxili (wait/notify)
Java/Kotlin nümunəsiReentrantLockSemaphore(permits)synchronized

Nə vaxt Mutex seçməli: bir resursu eyni vaxtda girişdən qorumaq lazımdır — məsələn, ortaq kolleksiya, fayl və ya sayğac. Nə vaxt Semaphore seçməli — resurs hovuzuna eyni vaxtda girişlərin sayını məhdudlaşdırmaq lazımdır, məsələn 5 bağlantı üçün verilənlər bazası hovuzu. Nə vaxt Monitor seçməli — şərti gözləmə ilə sinxronizasiya lazımdır, məsələn wait/notify ilə istehsalçı-istehlakçı növbəsi. Müasir Android inkişafında synchronized tez-tez ReentrantLock və ya kotlinx.coroutines Mutex ilə əvəz olunur.

Mutex istifadəsində tipik səhvlər

Finally-də unudulmuş unlock

Ən geniş yayılmış səhv — unlock() çağırışı üçün finally blokunun olmaması. Əgər kritik bölmədə istisna yaranarsa, Mutex bloklanmış qalır və digər thread-lər sonsuz gözləyir. İstisnaların mümkünsüz olduğuna əmin olsanız belə — həmişə try/finally və ya withLock istifadə edin. Bu, xüsusilə mobil inkişafda vacib olan defensive programming prinsipidir, burada istisnalar yaddaş çatışmazlığı və ya Configuration Changes səbəbindən yarana bilər.

Mutex-in fərqli ələ keçirmə ardıcıllığı

Tətbiqdə bir neçə Mutex istifadə edildikdə, onların ələ keçirilməsinin vahid ardıcıllığını təyin etmək kritikdir. Əgər Thread A M1 → M2, Thread B isə M2 → M1 ələ keçirirsə, Deadlock yaranır. Böyük layihələrdə (50 min sətirdən çox) kilidlərin ardıcıllığı memarlıq qərarında sənədləşdirilir və linterlərlə yoxlanılır. IntelliJ IDEA-da Lock Checker aləti kilidlərin ardıcıl olmayan ələ keçirilməsini avtomatik aşkarlayır.

Çox uzun kritik bölmə

Mutex-in 1-2 millisaniyədən artıq saxlanılması — səhv dizaynın əlamətidir. Kritik bölmə yalnız minimal zəruri əməliyyatları ehtiva etməlidir. Şəbəkə sorğuları, fayl giriş-çıxışı və mürəkkəb hesablamalar bloklanmış bölmədən kənarda icra edilməlidir. Android-də UI thread-də kilidin uzun müddət saxlanılması kadrların buraxılmasına (jank) və ANR-a gətirib çıxarır. Kritik bölmə əsasən oxuma əməliyyatlarından ibarətdirsə, ReadWriteLock istifadə edin.

Kotlin Coroutines-də Mutex

kotlinx.coroutines kitabxanası klassik ReentrantLock-dan əsaslı şəkildə fərqlənən öz Mutex tətbiqini təqdim edir. Əsas fərq — suspending Mutex ƏS thread-ni bloklamır, korutini kilid azad olana qədər dayandırır. Bu o deməkdir ki, cari korutin Mutex-i gözləyərkən thread digər korutinləri icra edə bilər.

kotlin
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock

class CoroutineCounter {
    private val mutex = Mutex()
    private var count = 0

    suspend fun increment() {
        mutex.withLock {  // suspending — thread-i bloklamır
            count++
        }
    }

    suspend fun getCount(): Int = mutex.withLock { count }
}

kotlinx Mutex-in əsas xüsusiyyətləri: qeyri-rekursivlik (non-reentrant) — ReentrantLock-dan fərqli olaraq, korutin sahib olduğu Mutex-i yenidən ələ keçirə bilməz. Bu lazımdırsa, Mutex əvəzinə Semaphore(1) istifadə edin. Bundan əlavə, kotlinx.coroutines-dən Mutex bloklamır: suspend vasitəsilə dayandırmadan istifadə edir ki, bu da hovuz thread-ni bloklamamağa imkan verir.

Praktikada suspending Mutex korutin kodunda klassik ReentrantLock-dan iki səbəbə görə üstündür: miqyaslama — bir korutin Mutex-i gözləyir, thread isə digər korutinləri xidmətləndirir, bu da sistemin ötürmə qabiliyyətini artırır; BlockedThread-in olmaması — bloklanmış thread-in yığınının saxlanmasına resurs sərf edilmir. JetBrains (Kotlin Coroutines Guide, 2024)-ə görə, suspending Mutex istifadəsi 100+ korutində ötürmə qabiliyyətini 40% artırır.

Tez-tez verilən suallar

Mutex ikili semaforadan nə ilə fərqlənir?

Sahiblik (ownership) — əsas fərqdir. Mutex hansı thread-in onu ələ keçirdiyini xatırlayır və yalnız bu thread onu azad edə bilər. İkili semafor (Semaphore(1))-un sahibi yoxdur — istənilən thread release() icra edə bilər. Buna görə Mutex daha təhlükəsizdir: başqa bir thread təsadüfən başqasının kilidini azad edə bilməz, semafor isə edə bilər.

Nə vaxt Mutex, nə vaxt synchronized istifadə etməli?

synchronized daha sadə və qısadır — onu vaxt məhdudiyyəti və ədalət nəzarəti olmayan sadə kritik bölmələr üçün istifadə edin. ReentrantLock-u vaxt məhdudiyyəti ilə TryLock, fair-planlaşdırma, Condition Variables və ya gözləyən thread-in kəsilməsi (lockInterruptibly) lazım olduqda istifadə edin. Korutinlər üçün həmişə kotlinx.coroutines.sync.Mutex istifadə edin.

Spinlock nədir və Mutex-dən nə ilə fərqlənir?

Spinlock — thread-in yuxulamadığı, dövrədə (spin) kilidin vəziyyətini yoxladığı blokadadır. Spinlock CPU sərf edir, lakin kontekst keçidi etmir, bu da onu qısa kritik bölmələr (10 təlimata qədər) üçün faydalı edir. Mutex thread-i BLOCKED vəziyyətinə keçirir, bu da kontekst keçidi səbəbindən 10-50 mikrosaniyə baha başa gəlir, lakin CPU sərf etmir.

Mutex ƏS səviyyəsində necə qurulub?

Linux nüvəsi səviyyəsində Mutex futex (fast userspace mutex) vasitəsilə tətbiq olunur. Thread əvvəlcə kilidi userspace-də atomik CAS (Compare-And-Swap) təlimatı ilə ələ keçirməyə çalışır. Mutex sərbəstdirsə — syscall olmadan ələ keçirmə baş verir. Məşğuldursa — thread futex(FUTEX_WAIT) syscall-ını edir və yuxulayır. Azad edildikdə futex(FUTEX_WAKE) syscall-ı bir gözləyən thread-i oyadır.

Mutex proseslərarası ola bilərmi?

Bəli, proseslərarası Mutex (inter-process mutex) mövcuddur. Windows-da bu Named Mutex, Linux-da — PTHREAD_PROCESS_SHARED atributu ilə pthread_mutexattr_setpshared. Android Bionic libc da fayl deskriptorları vasitəsilə proseslərarası Mutex-i dəstəkləyir. Proseslərarası Mutex müxtəlif tətbiqlər və ya proses ilə onun uşaq prosesləri arasında sinxronizasiya üçün istifadə olunur.

Nəticə

  • Mutex — yalnız bir thread-in eyni anda kritik bölməni icra etməsini təmin edən qarşılıqlı eksklüzivlik primitividir
  • Sahiblik (ownership) Mutex-i ikili semaforadan fərqləndirir — yalnız sahib thread azad edə bilər
  • ReentrantLock Java/Kotlin-də — rekursiv ələ keçirmə və TryLock dəstəyi ilə klassik Mutex tətbiqi
  • Finally bloku və ya withLock istisnalar zamanı Deadlock-un qarşısını almaq üçün məcburidir
  • kotlinx.coroutines-dən suspending Mutex ƏS thread-ni bloklamır, korutini dayandırır
  • Vahid ələ keçirmə ardıcıllığı bir neçə Mutex — mürəkkəb sistemlərdə Deadlock-dan qaçmağın yeganə yolu
  • Qısa kritik bölmələr (1-2 ms-dək) — Starvation olmadan çoxthreadli tətbiqlərin məhsuldarlığının açarı

Açar təslim mobil tətbiq hazırlayacağıq

IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.

Layihəni müzakirə et

Həm də oxuyun