Mobil inkişafda Thread — bu nədir, növləri və axınların idarə edilməsi

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

Thread — öz yığınına malik olan və digər axınlardan asılı olmayaraq işləyən prosessor vaxtının əsas vahididir. Mobil inkişafda axınlar uzunmüddətli əməliyyatlar zamanı interfeysin cavabdeh qalması üçün tapşırıqların paralel icrasında istifadə olunur. Android java.lang.Thread, Executors və Kotlin Coroutines dəstəkləyir, iOS — Thread (Objective-C), GCD və OperationQueue. Android Thread Documentation-a görə, nativ axın yaratmaq üçün əməliyyat sistemi tərəfindən yığına ~1 MB ayrılması tələb olunur.

Əsas məqamlar

  • Thread — CPU planlaşdırmasının minimal vahidi: hər axın müstəqildir və öz yığınına malikdir
  • Axın yaratmaq Android-də yığın üçün ~1 MB, iOS-da 512 KB tələb edir, buna görə hovuzlar birbaşa yaratmadan daha effektivdir
  • Android: Thread, Executors, HandlerThread, Coroutines — axın abstraksiyasının dörd səviyyəsi
  • iOS: Thread (aşağı səviyyəli), GCD (DispatchQueue), OperationQueue (yüksək səviyyəli)
  • Thread safety — mutable məlumatlara ümumi giriş sinxronizasiya tələb edir: locks, atomic, serial queues

Thread nədir

Thread (icra axını) — əməliyyat sisteminin CPU nüvəsində planlaşdıra bildiyi müstəqil təlimatlar ardıcıllığıdır. Hər bir proses (proqram) ən azı bir axın ehtiva edir — Main Thread. Əlavə axınlar tapşırıqların paralel icrası üçün yaradılır. Hər axının öz proqram yığını (lokal dəyişənlərlə), əmr sayğacı (PC) və registrləri var. Heap yaddaşı prosesin bütün axınları üçün ümumidir.

Mobil əməliyyat sistemlərində axınlar qabaqlayıcı multitasking (preemptive multitasking) ilə planlaşdırılır: ƏS axının icrasını istənilən an kəsib idarəetməni digərinə verə bilər (context switch). Kontekst keçidi bahalı əməliyyatdır (1–10 mikrosaniyə), çünki CPU registrlərinin saxlanması/bərpası, TLB-nin yenilənməsi və keşlərin təmizlənməsi tələb olunur. Məhz buna görə həddindən artıq çox axın (yüzlərlə və minlərlə) performansı pisləşdirir — ƏS icraya deyil, keçidə daha çox vaxt sərf edir.

Axın və proses — fərqli anlayışlardır. Proses ayrılmış virtual yaddaşa malik proqram nüsxəsidir. Proses daxilindəki axın bu yaddaşı digər axınlarla bölüşür. Android-də proqramın hər komponenti (Activity, Service, BroadcastReceiver) bir prosesdə işləyir, lakin müxtəlif axınlarda icra oluna bilər. iOS proqramı da GCD və ya Thread vasitəsilə əlavə axınlar yaratmaq imkanı olan bir prosesdir.

Axının həyat dövrü: vəziyyətlər və keçidlər

Hər axın Java/Kotlin (Android) və NSThread (iOS) üçün beş vəziyyətdən keçir: New (yaradılıb), Runnable (icraya hazırdır), Running (CPU-da icra olunur), Blocked/Waiting (resurs və ya bildiriş gözləyir), Terminated (tamamlanıb). Vəziyyətlər arası keçidlər ƏS planlayıcısı və sinxronizasiya primitivləri tərəfindən idarə olunur. Tərtibatçı axının prioritetinə (Thread.setPriority()) və vəziyyətinə (sleep, join, interrupt) təsir edə bilər.

Android-də axın məşğul monitoru (synchronized) ələ keçirməyə cəhd edəndə, Object.wait() və ya Thread.sleep() çağırarkən Blocked vəziyyətinə keçir. iOS-da — NSCondition.wait(), pthread_cond_wait() və ya dispatch_semaphore_wait() çağırarkən. Blocked vəziyyətində axın CPU sərf etmir, lakin yaddaş tutur (yığın). Axın digər axından kəsilə bilər (interrupted), InterruptedException (Java) alaraq və ya isCancelled (Kotlin Coroutines) yoxlayaraq.

Vəziyyət Təsvir Keçid metodu
New Axın yaradılıb, lakin işə salınmayıb Thread() konstruktoru
Runnable Axın icraya hazırdır, CPU gözləyir thread.start()
Running Axın CPU nüvəsində icra olunur ƏS planlayıcısı
Blocked/Waiting Axın resurs, monitor və ya bildiriş gözləyir synchronized, wait(), sleep()
Terminated Axın run()-u bitirib və ya kəsilib run() tamamlandı, interrupt()

Context Switch və onun qiyməti

Context switch (kontekst keçidi) — ƏS-nin cari axının vəziyyətini (registrlər, PC, TLB) saxladığı və digərinin saxlanmış vəziyyətini yüklədiyi əməliyyatdır. Mobil sistemlərdə (Linux + ART, iOS üçün XNU) context switch 1–10 mikrosaniyə çəkir. Əgər axın tapşırığı 100 mikrosaniyəyə icra edirsə, context switch isə 5 çəkir, o zaman vaxtın 5%-i boşa gedir. Context switch-i minimuma endirmək üçün iOS work stealing ilə GCD-dən, Android isə fixedThreadCount hovuzlarından istifadə edir.

Android-də Thread: Thread-dan Coroutines-a qədər

Android aşağı səviyyəli java.lang.Thread-dan müasir korutinlərə qədər təkamül keçib. Abstraksiyanın hər səviyyəsi daha az əlavə xərclə daha çox imkan verir. Thread — baza sinfidir, lakin onun birbaşa yaradılması tövsiyə olunmur: yeni axın hovuz tərəfindən idarə olunmur, onu izləmək və ləğv etmək çətindir. AsyncTask (API 30-dan deprecated) irəli addım idi, lakin yaddaş sızmasından və konfiqurasiyaların rahatsız emalından əziyyət çəkirdi.

HandlerThread — mesaj növbəsini emal edə bilən Looper-li Thread xüsusi alt sinfidir. Fondan axınında tapşırıqların ardıcıl icrası üçün istifadə olunur, məsələn, məlumatların Room-a və ya fayllara yazılması. HandlerThread start() çağırışı ilə yaradılır, sonra Handler(handlerThread.looper) vasitəsilə mesajlar və Runnable göndərilə bilər. handlerThread.quit() çağırışı Looper-i dayandırır və axını bitirir.

kotlin
// Android: Thread, HandlerThread və Executors
import android.os.Handler
import android.os.HandlerThread
import java.util.concurrent.Executors

class ThreadExample {

    // 1. Thread-in birbaşa yaradılması (tövsiyə olunmur)
    fun directThread() {
        val thread = Thread(Runnable {
            Thread.sleep(1000)
            print("Birbaşa axın icra olundu")
        })
        thread.start()
    }

    // 2. Ardıcıl fon tapşırıqları üçün HandlerThread
    fun handlerThreadExample() {
        val handlerThread = HandlerThread("BackgroundQueue")
        handlerThread.start()

        val handler = Handler(handlerThread.looper)
        handler.post {
            // Fon axınında ardıcıl icra
            Thread.sleep(500)
            print("HandlerThread: tapşırıq icra olundu")
        }

        // Axının dayandırılması (tapşırıqlar bitdikdə icra olunur)
        handlerThread.quitSafely()
    }

    // 3. Executors — axın hovuzu
    fun executorExample() {
        val executor = Executors.newFixedThreadPool(4)
        for (i in 1..10) {
            executor.execute {
                print("Task $i on thread ${Thread.currentThread().getName()}")
            }
        }
        executor.shutdown()
    }

    // 4. Kotlin Coroutines — müasir standart
    suspend fun coroutineExample() = kotlinx.coroutines.withContext(
        kotlinx.coroutines.Dispatchers.Default
    ) {
        print("Coroutine on thread: ${Thread.currentThread().getName()}")
    }
}

ThreadExample nümunəsi Android-də axın abstraksiyasının bütün dörd səviyyəsini göstərir. Thread-in birbaşa yaradılması — ən aşağı səviyyəli və səmərəsiz yanaşmadır. HandlerThread fonda ardıcıl tapşırıqlar üçün faydalıdır. Executors.newFixedThreadPool(4) 10 tapşırığın paralel icrası üçün 4 axından ibarət hovuz yaradır. Dispatchers.Default ilə Kotlin Coroutines — müasir, effektiv və təhlükəsiz üsuldur.

HandlerThread: ardıcıl fon tapşırıqları

HandlerThread — daxili Looper və mesaj növbəsi olan xüsusi Thread alt sinfidir. start() çağırışı ilə yaradılır, sonra Handler(handlerThread.looper) vasitəsilə Runnable və mesajlar göndərilə bilər. HandlerThread tapşırıqları ciddi ardıcıllıqla icra edir — növbəti tapşırıq əvvəlki tamamlanana qədər başlamır. Bu, əməliyyat sırasının kritik olduğu Room-a və ya fayllara məlumat yazarkən rahatdır. quitSafely() çağırışı cari tapşırıq bitdikdən sonra Looper-i dayandırır.

iOS-da Thread: Thread, GCD və OperationQueue

iOS da axınlarla işin üç səviyyəsini təqdim edir. Thread (Swift-də Thread, Objective-C-də NSThread) — nativ axın birbaşa yaradan aşağı səviyyəli API-dir. DispatchQueue vasitəsilə GCD (Grand Central Dispatch) — axın hovuzunu avtomatik idarə edən iOS tərtibatçıları üçün əsas vasitədir. OperationQueue — asılılıqlar, prioritetlər və ləğv dəstəyi ilə GCD üzərində yüksək səviyyəli abstraksiyadır.

Müasir iOS inkişafında Thread-in birbaşa istifadəsi olduqca nadirdir — GCD yaddaş və axınların avtomatik idarə edilməsi ilə bütün lazımi imkanları təmin edir. Thread yalnız xüsusi hallarda istifadə olunur: thread-local storage (threadDictionary) quraşdırılması, fon axını üçün RunLoop yaradılması və ya pthread_t gözləyən C kitabxanaları ilə inteqrasiya.

swift
import Foundation

class ThreadManager {

    // 1. Thread (aşağı səviyyəli)
    func createThread() {
        let thread = Thread {
            // Kod yeni axında icra olunur
            print("Current thread: \(Thread.current)")
        }
        thread.name = "com.app.worker"
        thread.qualityOfService = .utility
        thread.start()
    }

    // 2. GCD — DispatchQueue
    func gcdExample() {
        // Paralel növbə
        let queue = DispatchQueue(label: "com.app.concurrent",
                                 qos: .utility,
                                 attributes: .concurrent)

        queue.async {
            print("GCD async tapşırığı")
        }

        // Yazmanın sinxronizasiyası üçün Barrier
        queue.async(flags: .barrier) {
            // Yazma zamanı eksklüziv giriş
            print("Barrier write: eksklüziv giriş")
        }
    }

    // 3. Asılılıqlı OperationQueue
    func operationQueueExample() {
        let queue = OperationQueue()
        queue.maxConcurrentOperationCount = 2
        queue.qualityOfService = .background

        let download = BlockOperation {
            print("Yüklənir...")
        }
        let process = BlockOperation {
            print("Emal olunur...")
        }
        let save = BlockOperation {
            print("Yazılır...")
        }

        // Asılılıqlar: download -> process -> save
        process.addDependency(download)
        save.addDependency(process)

        queue.addOperations([download, process, save], waitUntilFinished: false)
    }
}

// GCD barrier vasitəsilə thread-safe kolleksiya
class ThreadSafeArray<T> {
    private var array: [T] = []
    private let queue = DispatchQueue(label: "com.app.concurrent",
                                       attributes: .concurrent)

    var count: Int {
        return queue.sync { array.count } // concurrent read
    }

    func append(_ element: T) {
        queue.async(flags: .barrier) { // exclusive write
            self.array.append(element)
        }
    }
}

ThreadSafeArray sinfi GCD barrier vasitəsilə Concurrent Read / Exclusive Write nümunəsini nümayiş etdirir. queue.sync{} ilə oxuma bir neçə axından paralel icra olunur. queue.async(flags: .barrier) ilə yazma yazma tamamlanana qədər bütün digər əməliyyatları (həm oxuma, həm yazma) bloklayır. Bu, synchronized bloklardan daha effektivdir, çünki yazma olmadıqda oxucuları bloklamır.

iOS Thread vs GCD: Thread-i birbaşa nə zaman istifadə etmək lazımdır

iOS-da Thread-in birbaşa istifadəsi üç halda əsaslandırılır: axınla əlaqəli məlumatların saxlanması üçün thread-local storage (Thread.current.threadDictionary); performSelector:onThread: ilə fon axınında xüsusi RunLoop yaradılması; pthread_t gözləyən C/C++ kitabxanaları ilə inteqrasiya. Bütün digər hallarda DispatchQueue vasitəsilə GCD daha üstündür — axın hovuzunu və enerji istehlakını avtomatik idarə edir.

Axınların sinxronizasiyası: locks, atomic, serial queues

Race condition (yarış vəziyyəti) iki və daha çox axın eyni zamanda ümumi məlumatlara daxil olduqda və onlardan ən azı biri yazma icra etdikdə yaranır. Nəticə icra sırasından (timing) asılıdır və qeyri-müəyyəndir. Race condition-un qarşısını almaq üçün sinxronizasiya primitivlərindən istifadə olunur. Mobil inkişafda bloklamalar (synchronized, NSLock), atomik əməliyyatlar (AtomicInteger, iOS atomic xassələri) və növbələr (serial queue) mövcuddur.

Primitiv seçimi ssenaridən asılıdır. Sadə sayğaclar və flaqelər üçün atomik əməliyyatlar (AtomicInteger, atomic property) kifayətdir. Bir neçə əməliyyatlı kritik bölmələr üçün — bloklamalar (synchronized, NSLock). Mürəkkəb məlumat strukturları üçün — serial DispatchQueue və ya GCD barrier. Bloklamaları anlamaq asandır, lakin onlar deadlock və livelock-a məruz qalır. Növbələr daha çətindir, lakin daha təhlükəsizdir.

kotlin
// Android/Kotlin-də sinxronizasiya
import java.util.concurrent.atomic.AtomicInteger
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock

class Counter {

    // 1. AtomicInteger — sadə sayğaclar üçün
    private val atomicCount = AtomicInteger(0)
    fun incrementAtomic() = atomicCount.incrementAndGet()

    // 2. synchronized — kritik bölmələr üçün
    @Synchronized
    fun synchronizedOperation() {
        // Bir anda yalnız bir axın
        doWork()
    }

    // 3. Korutinlərdən Mutex — suspend-safe
    private val mutex = Mutex()
    suspend fun mutexOperation() {
        mutex.withLock {
            // protected code — thread-safe
            doWork()
        }
    }

    private fun doWork() { /* critical section */ }
}

// Deadlock nümunəsi: A B-ni bloklayır, B A-nı bloklayır
class DeadlockExample {
    private val lockA = Any()
    private val lockB = Any()

    fun methodA() = synchronized(lockA) {
        Thread.sleep(100)
        synchronized(lockB) { print("OK") }
    }

    fun methodB() = synchronized(lockB) {
        Thread.sleep(100)
        synchronized(lockA) { print("OK") }
    }
}

Counter sinxronizasiyaya üç yanaşmanı nümayiş etdirir. AtomicInteger.incrementAndGet() — bloklamasız atomik əməliyyatdır (CAS). @Synchronized — Java-nın daxili monitorudur, bütün obyekti bloklayır. Mutex.withLock — korutin mutexi, axını bloklamaq əvəzinə korutini dayandırır (daha effektivdir). DeadlockExample klassik deadlock-u göstərir: iki axın bloklamaları müxtəlif ardıcıllıqla ələ keçirir.

Thread Hovuzları: niyə Executors Thread-dan daha yaxşıdır

Thread Pool (axın hovuzu) — tapşırıqların icrası üçün təkrar istifadə olunan əvvəlcədən yaradılmış axınlar toplusudur. Hər tapşırıq üçün yeni axın yaratmaq əvəzinə (bahalıdır) hovuz boş axını götürür. Boş axın yoxdursa, tapşırıq növbəyə qoyulur. Hovuz ölçüsünü avtomatik idarə edir: pik yüklənmədə yeni axınlar yaradılır, boş axınlar tamamlanır. Bu, axın yaratmaq xərclərini on dəfələrlə azaldır.

Android-də Executors.newFixedThreadPool(4) 4 axından ibarət hovuz yaradır. Eyni anda 10 tapşırıq gəlirsə, 4-ü dərhal başlayacaq, 6-sı növbədə gözləyəcək. Executors.newCachedThreadPool() ehtiyac olduqca (limitsiz) axınlar yaradır və boş qalanları 60 saniyədən sonra tamamlayır. iOS üçün GCD ölçüsü CPU nüvələrinin sayına və cari yüklənməyə uyğun olan qlobal növbələr hovuzunu avtomatik təmin edir.

Kotlin Coroutines-də axın hovuzları dispetçerlərin içində gizlədilib. Dispatchers.Default ölçüsü CPU nüvələrinin sayına bərabər (ən azı 2) olan hovuzdan istifadə edir. Dispatchers.IO — 64 axın (yüzlərlə IO-bound tapşırıq üçün kifayətdir, çünki çoxu CPU tutmadan giriş-çıxışı gözləyəcək). Hər dispetçer hovuzu yükə uyğun avtomatik miqyaslayır, boş vaxtda batareya enerjisinə qənaət edir.

Tez-tez verilən suallar

Mobil inkişafda Thread nədir?

Thread — proqramda kodun icrasının əsas vahididir. Hər proses yaddaşı bölüşən, lakin öz yığınına malik olan çoxlu axına sahib ola bilər. Mobil inkişafda axınlar UI-ni bloklamadan tapşırıqların paralel icrası üçün istifadə olunur. Android Thread, Executors, HandlerThread və Coroutines istifadə edir. iOS Thread, GCD (DispatchQueue) və OperationQueue istifadə edir.

Niyə Thread-i birbaşa yaratmaq tövsiyə olunmur?

Thread yaratmaq Android-də yığın üçün ~1 MB, iOS-da ~512 KB ayrılmasını tələb edir — bu bahalı əməliyyatdır. 1000 tapşırıq üçün 1000 axının birbaşa yaradılması yalnız yığınlara ~1 GB üstəgəl context switch-ə əlavə xərclər tələb edəcək. Thread əvəzinə hovuzlardan (Executors, GCD) və ya korutinlərdən istifadə edin — onlar axınları təkrar istifadə edir, xərcləri on dəfələrlə azaldır.

Race condition nədir və ondan necə qaçınmaq olar?

Race condition — bir neçə axının yazma ilə ümumi məlumatlara eyni vaxtda daxil olması zamanı qeyri-müəyyən davranışdır. Ondan üç yolla qaçmaq olar: atomik tiplərdən (AtomicInteger), bloklamalardan (synchronized, NSLock) istifadə etmək və ya girişi növbə vasitəsilə (DispatchQueue serial, Kotlin-də Actor) serializasiya etmək. Ən yaxşı təcrübə — ümumi mutable vəziyyəti minimuma endirmək və immutability istifadə etməkdir.

Thread korutindən nə ilə fərqlənir?

Thread — ~1 MB yığın tutan və ƏS nüvəsinə bağlı olan nativ sistem obyektidir. Korutin — konkret axına bağlı olmayan və bloklamadan dayandırıla bilən (suspend) yüngül Kotlin icra vahididir. Bir axın minlərlə korutini icra edə bilər. Korutinlər yaddaş baxımından daha effektivdir və callbacks olmadan asinxron kod yazmağa imkan verir.

Mobil proqramda deadlock-u necə tutmaq olar?

Deadlock ANR olmadan proqramın tam asılması kimi özünü göstərir. Android-də bütün axınların yığınlarının dump-u üçün Thread.getAllStackTraces() istifadə edin — iki axın bir-birinin bloklamasını gözləyəcək. iOS-da — Thread.callStackSymbols. Alətlər: Android Studio Profiler (Threads tab), Instruments (iOS, Thread State View). Qarşısının alınması: bloklamaları sabit ardıcıllıqla tutun, time-out ilə tryLock istifadə edin.

Nəticələr

  • Thread — CPU-nun minimal vahidi: öz yığını ilə müstəqil icra, heap yaddaşı ümumidir
  • Beş vəziyyət: New, Runnable, Running, Blocked/Waiting, Terminated
  • Android Thread → AsyncTask → Executors → HandlerThread → Coroutines istiqamətində təkamül keçib
  • iOS aşağıdan yüksək səviyyəyə Thread, GCD (DispatchQueue) və OperationQueue təqdim edir
  • Race condition bloklamalar (synchronized, NSLock), atomik tiplər və serial-növbələrlə həll olunur
  • Deadlock bloklamaların çarpaz ələ keçirilməsində yaranır — sabit ardıcıllıqla qarşısı alınır
  • Thread Pool yeni Thread yaratmaqdan daha effektivdir: axınları təkrar istifadə edir, context switch xərclərini azaldır

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