Thread — o'z stekiga ega bo'lgan va boshqa oqimlardan mustaqil ravishda ishlaydigan protsessor vaqtining asosiy birligidir. Mobil ishlab chiqishda oqimlar uzoq davom etadigan operatsiyalar paytida interfeys sezgir bo'lib qolishi uchun vazifalarni parallel bajarishda ishlatiladi. Android java.lang.Thread, Executors va Kotlin Coroutines-ni, iOS — Thread (Objective-C), GCD va OperationQueue-ni qo'llab-quvvatlaydi. Android Thread Documentation ma'lumotlariga ko'ra, nativ oqim yaratish uchun operatsion tizim tomonidan stek uchun ~1 MB ajratilishi kerak.
Asosiy narsalar
Thread (bajarish oqimi) — operatsion tizim CPU yadrosida rejalashtirishi mumkin bo'lgan mustaqil ko'rsatmalar ketma-ketligi. Har bir jarayon (ilova) kamida bitta oqimni o'z ichiga oladi — Main Thread. Qo'shimcha oqimlar vazifalarni parallel bajarish uchun yaratiladi. Har bir oqim o'zining dasturiy stekiga (lokal o'zgaruvchilar bilan), buyruq hisoblagichiga (PC) va registrlarga ega. Heap xotirasi jarayonning barcha oqimlari uchun umumiy hisoblanadi.
Mobil operatsion tizimlarda oqimlar preemptive multitasking (majburiy ko'p vazifalilik) bilan rejalashtiriladi: operatsion tizim istalgan paytda oqim bajarilishini to'xtatib, boshqaruvni boshqasiga berishi mumkin (context switch). Kontekst almashinuvi qimmat operatsiya (1–10 mikrosekund), chunki CPU registrlarini saqlash/tiklash, TLB ni yangilash va keshlarni tozalash kerak. Aynan shuning uchun haddan tashqari ko'p oqim (yuzlab va minglab) ishlashni yomonlashtiradi — operatsion tizim bajarishdan ko'ra almashinishga ko'proq vaqt sarflaydi.
Oqim va jarayon — turli tushunchalar. Jarayon ajratilgan virtual xotiraga ega ilova nusxasi. Jarayon ichidagi oqim bu xotirani boshqa oqimlar bilan bo'lishadi. Android-da ilovaning har bir komponenti (Activity, Service, BroadcastReceiver) bitta jarayonda ishlaydi, lekin turli oqimlarda bajarilishi mumkin. iOS ilovasi ham GCD yoki Thread orqali qo'shimcha oqimlar yaratish imkoniyatiga ega bo'lgan bitta jarayondir.
Har bir oqim Java/Kotlin (Android) va NSThread (iOS) da besh holatdan o'tadi: New (yaratilgan), Runnable (bajarishga tayyor), Running (CPU-da bajarilmoqda), Blocked/Waiting (resurs yoki xabarnoma kutmoqda), Terminated (tugatilgan). Holatlar orasidagi o'tishlarni operatsion tizim rejalashtiruvchisi va sinxronizatsiya primitivlari boshqaradi. Dasturchi oqimning ustuvorligiga (Thread.setPriority()) va holatiga (sleep, join, interrupt) ta'sir qilishi mumkin.
Android-da oqim band monitor (synchronized)ni egallashga urinayotganda, Object.wait() yoki Thread.sleep() chaqirilganda Blocked holatiga o'tadi. iOS-da — NSCondition.wait(), pthread_cond_wait() yoki dispatch_semaphore_wait() chaqirilganda. Blocked holatida oqim CPU sarflamaydi, lekin xotira (stek) egallaydi. Oqim boshqa oqimdan uzilishi mumkin (interrupted), InterruptedException (Java) olib yoki isCancelled (Kotlin Coroutines) ni tekshirib.
| Holat | Tavsif | O'tish usuli |
|---|---|---|
| New | Oqim yaratilgan, lekin ishga tushirilmagan | Thread() konstruktori |
| Runnable | Oqim bajarishga tayyor, CPU kutyapti | thread.start() |
| Running | Oqim CPU yadrosida bajarilmoqda | Operatsion tizim rejalashtiruvchisi |
| Blocked/Waiting | Oqim resurs, monitor yoki xabarnoma kutyapti | synchronized, wait(), sleep() |
| Terminated | Oqim run()-ni tugatdi yoki uzildi | run() tugadi, interrupt() |
Context switch (kontekst almashinuvi) — operatsion tizim joriy oqimning holatini (registrlar, PC, TLB) saqlab, boshqasining saqlangan holatini yuklaydigan operatsiya. Mobil tizimlarda (Linux + ART, iOS uchun XNU) context switch 1–10 mikrosekund davom etadi. Agar oqim vazifani 100 mikrosekundda bajarsa, context switch esa 5 da davom etsa, vaqtning 5 foizi behuda ketadi. Context switch-ni minimallashtirish uchun iOS work stealing bilan GCD-dan, Android esa fixedThreadCount hovuzlaridan foydalanadi.
Android past darajadagi java.lang.Thread-dan zamonaviy korutinlargacha evolyutsiyani bosib o'tdi. Abstraksiyaning har bir darajasi kamroq qo'shimcha xarajatlar bilan ko'proq imkoniyat beradi. Thread — asosiy sinf, lekin uni to'g'ridan-to'g'ri yaratish tavsiya etilmaydi: yangi oqim hovuz tomonidan boshqarilmaydi, uni kuzatish va bekor qilish qiyin. AsyncTask (API 30 dan deprecated) oldinga qadam edi, lekin xotira oqishidan va konfiguratsiyalarni noqulay qayta ishlashdan aziyat chekardi.
HandlerThread — xabarlar navbatini qayta ishlay oladigan Looper-li Thread maxsus kichik sinfi. Fon oqimida vazifalarni ketma-ket bajarish uchun ishlatiladi, masalan, ma'lumotlarni Room-ga yoki fayllarga yozish. HandlerThread start() chaqiruvi bilan yaratiladi, so'ngra Handler(handlerThread.looper) orqali xabarlar va Runnable yuborish mumkin. handlerThread.quit() chaqiruvi Looper-ni to'xtatadi va oqimni tugatadi.
// Android: Thread, HandlerThread va Executors
import android.os.Handler
import android.os.HandlerThread
import java.util.concurrent.Executors
class ThreadExample {
// 1. Thread-ni to'g'ridan-to'g'ri yaratish (tavsiya etilmaydi)
fun directThread() {
val thread = Thread(Runnable {
Thread.sleep(1000)
print("To'g'ridan-to'g'ri oqim bajarildi")
})
thread.start()
}
// 2. Ketma-ket fon vazifalari uchun HandlerThread
fun handlerThreadExample() {
val handlerThread = HandlerThread("BackgroundQueue")
handlerThread.start()
val handler = Handler(handlerThread.looper)
handler.post {
// Fon oqimida ketma-ket bajarish
Thread.sleep(500)
print("HandlerThread: vazifa bajarildi")
}
// Oqimni to'xtatish (vazifalar tugaganda bajariladi)
handlerThread.quitSafely()
}
// 3. Executors — oqimlar hovuzi
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 — zamonaviy standart
suspend fun coroutineExample() = kotlinx.coroutines.withContext(
kotlinx.coroutines.Dispatchers.Default
) {
print("Coroutine on thread: ${Thread.currentThread().getName()}")
}
}
ThreadExample namunasi Android-da oqim abstraksiyasining barcha to'rt darajasini ko'rsatadi. Thread-ni to'g'ridan-to'g'ri yaratish — eng past darajali va samarasiz yondashuv. HandlerThread fonda ketma-ket vazifalar uchun foydali. Executors.newFixedThreadPool(4) 10 tagacha vazifani parallel bajarish uchun 4 oqimdan iborat hovuz yaratadi. Dispatchers.Default bilan Kotlin Coroutines — zamonaviy, samarali va xavfsiz usul.
HandlerThread — ichki Looper va xabarlar navbatiga ega Thread ixtisoslashgan kichik sinfi. start() chaqiruvi bilan yaratiladi, so'ngra Handler(handlerThread.looper) orqali Runnable va xabarlar yuborilishi mumkin. HandlerThread vazifalarni qat'iy ketma-ket bajaradi — keyingi vazifa avvalgisi tugaguncha boshlanmaydi. Bu operatsiyalar tartibi muhim bo'lgan Room-ga yoki fayllarga ma'lumot yozishda qulay. quitSafely() chaqiruvi joriy vazifa tugagandan so'ng Looper-ni to'xtatadi.
iOS ham oqimlar bilan ishlashning uch darajasini taqdim etadi. Thread (Swift-da Thread, Objective-C-da NSThread) — nativ oqimni to'g'ridan-to'g'ri yaratadigan past darajali API. DispatchQueue orqali GCD (Grand Central Dispatch) — oqimlar hovuzini avtomatik boshqaradigan iOS dasturchilari uchun asosiy vosita. OperationQueue — bog'liqliklar, ustuvorliklar va bekor qilishni qo'llab-quvvatlaydigan GCD ustidagi yuqori darajali abstraksiya.
Zamonaviy iOS ishlab chiqishda Thread-dan to'g'ridan-to'g'ri foydalanish juda kam uchraydi — GCD xotira va oqimlarni avtomatik boshqarish bilan barcha kerakli imkoniyatlarni ta'minlaydi. Thread faqat maxsus holatlarda ishlatiladi: thread-local storage (threadDictionary) o'rnatish, fon oqimi uchun RunLoop yaratish yoki pthread_t kutyotgan C kutubxonalari bilan integratsiya.
import Foundation
class ThreadManager {
// 1. Thread (past darajali)
func createThread() {
let thread = Thread {
// Kod yangi oqimda bajariladi
print("Current thread: \(Thread.current)")
}
thread.name = "com.app.worker"
thread.qualityOfService = .utility
thread.start()
}
// 2. GCD — DispatchQueue
func gcdExample() {
// Parallel navbat
let queue = DispatchQueue(label: "com.app.concurrent",
qos: .utility,
attributes: .concurrent)
queue.async {
print("GCD async vazifa")
}
// Yozishni sinxronlash uchun Barrier
queue.async(flags: .barrier) {
// Yozish paytida eksklyuziv kirish
print("Barrier write: eksklyuziv kirish")
}
}
// 3. Bog'liqlikli OperationQueue
func operationQueueExample() {
let queue = OperationQueue()
queue.maxConcurrentOperationCount = 2
queue.qualityOfService = .background
let download = BlockOperation {
print("Yuklab olinmoqda...")
}
let process = BlockOperation {
print("Qayta ishlanmoqda...")
}
let save = BlockOperation {
print("Saqlanmoqda...")
}
// Bog'liqliklar: download -> process -> save
process.addDependency(download)
save.addDependency(process)
queue.addOperations([download, process, save], waitUntilFinished: false)
}
}
// GCD barrier orqali 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 orqali Concurrent Read / Exclusive Write namunasini ko'rsatadi. queue.sync{} orqali o'qish bir nechta oqimdan parallel bajariladi. queue.async(flags: .barrier) orqali yozish yozish tugaguncha barcha boshqa operatsiyalarni (ham o'qish, ham yozish) bloklaydi. Bu synchronized bloklardan samaraliroq, chunki yozish bo'lmaguncha o'quvchilarni bloklamaydi.
iOS-da Thread-dan to'g'ridan-to'g'ri foydalanish uchta holatda oqlanadi: oqim bilan bog'liq ma'lumotlarni saqlash uchun thread-local storage (Thread.current.threadDictionary); performSelector:onThread: bilan fon oqimida maxsus RunLoop yaratish; pthread_t kutyotgan C/C++ kutubxonalari bilan integratsiya. Qolgan barcha holatlarda DispatchQueue orqali GCD afzalroq — u oqimlar hovuzini va energiya sarfini avtomatik boshqaradi.
Race condition (poyga holati) ikki yoki undan ortiq oqim bir vaqtning o'zida umumiy ma'lumotlarga kirganda va ulardan kamida bittasi yozishni bajarganda yuzaga keladi. Natija bajarish tartibiga (timing) bog'liq va oldindan aytib bo'lmaydi. Race condition-ni oldini olish uchun sinxronizatsiya primitivlari ishlatiladi. Mobil ishlab chiqishda blokirovkalar (synchronized, NSLock), atomik operatsiyalar (AtomicInteger, iOS atomic xossalari) va navbatlar (serial queue) mavjud.
Primitiv tanlash ssenariyga bog'liq. Oddiy hisoblagichlar va bayroqlar uchun atomik operatsiyalar (AtomicInteger, atomic property) yetarli. Bir nechta operatsiyali kritik bo'limlar uchun — blokirovkalar (synchronized, NSLock). Murakkab ma'lumot tuzilmalari uchun — serial DispatchQueue yoki GCD barrier. Blokirovkalarni tushunish oson, lekin ular deadlock va livelock-ka moyil. Navbatlar murakkabroq, lekin xavfsizroq.
// Android/Kotlin-da sinxronizatsiya
import java.util.concurrent.atomic.AtomicInteger
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
class Counter {
// 1. AtomicInteger — oddiy hisoblagichlar uchun
private val atomicCount = AtomicInteger(0)
fun incrementAtomic() = atomicCount.incrementAndGet()
// 2. synchronized — kritik bo'limlar uchun
@Synchronized
fun synchronizedOperation() {
// Bir vaqtda faqat bitta oqim
doWork()
}
// 3. Korutinlardan Mutex — suspend-safe
private val mutex = Mutex()
suspend fun mutexOperation() {
mutex.withLock {
// protected code — thread-safe
doWork()
}
}
private fun doWork() { /* critical section */ }
}
// Deadlock namunasi: A B-ni bloklaydi, B A-ni bloklaydi
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 sinxronizatsiyaga uch yondashuvni ko'rsatadi. AtomicInteger.incrementAndGet() — blokirovkalarsiz atomik operatsiya (CAS). @Synchronized — Java-ning ichki monitori, butun obyektni bloklaydi. Mutex.withLock — korutin muteksi, oqimni bloklash o'rniga korutinni to'xtatadi (samaraliroq). DeadlockExample klassik deadlock-ni ko'rsatadi: ikkita oqim blokirovkalarni turli tartibda egallaydi.
Thread Pool (oqimlar hovuzi) — vazifalarni bajarish uchun qayta ishlatiladigan oldindan yaratilgan oqimlar to'plami. Har bir vazifa uchun yangi oqim yaratish o'rniga (qimmat) hovuz bo'sh oqimni oladi. Bo'sh oqim bo'lmasa, vazifa navbatga qo'yiladi. Hovuz hajmini avtomatik boshqaradi: pik yuklanishda yangi oqimlar yaratiladi, bo'sh oqimlar tugatiladi. Bu oqim yaratish xarajatlarini o'n barobar kamaytiradi.
Android-da Executors.newFixedThreadPool(4) 4 oqimdan iborat hovuz yaratadi. Bir vaqtning o'zida 10 vazifa kelsa, 4 tasi darhol boshlanadi, 6 tasi navbatda kutadi. Executors.newCachedThreadPool() ehtiyojga qarab (cheklovsiz) oqimlar yaratadi va bo'sh qolganlarini 60 soniyadan keyin tugatadi. iOS uchun GCD o'lchami CPU yadrolari soniga va joriy yuklanishga mos keladigan global navbatlar hovuzini avtomatik ta'minlaydi.
Kotlin Coroutines-da oqim hovuzlari dispetcherlar ichida yashiringan. Dispatchers.Default o'lchami CPU yadrolari soniga teng (kamida 2) hovuzdan foydalanadi. Dispatchers.IO — 64 oqim (yuzlab IO-bound vazifalar uchun yetarli, chunki ko'pchiligi CPU egallamasdan kirish-chiqishni kutadi). Har bir dispetcher hovuzni yuklanishga qarab avtomatik kengaytiradi, bo'sh vaqtda batareya energiyasini tejaydi.
Tez-tez so'raladigan savollar
Thread — ilovada kod bajarilishining asosiy birligi. Har bir jarayon xotirani bo'lishadigan, lekin o'z stekiga ega bo'lgan ko'plab oqimlarga ega bo'lishi mumkin. Mobil ishlab chiqishda oqimlar UI-ni bloklamasdan vazifalarni parallel bajarish uchun ishlatiladi. Android Thread, Executors, HandlerThread va Coroutines-dan foydalanadi. iOS Thread, GCD (DispatchQueue) va OperationQueue-dan foydalanadi.
Thread yaratish Android-da stek uchun ~1 MB, iOS-da ~512 KB ajratishni talab qiladi — bu qimmat operatsiya. 1000 vazifa uchun 1000 oqimni to'g'ridan-to'g'ri yaratish faqat steklarga ~1 GB va context switch uchun qo'shimcha xarajatlarni talab qiladi. Thread o'rniga hovuzlardan (Executors, GCD) yoki korutinlardan foydalaning — ular oqimlarni qayta ishlatadi, xarajatlarni o'n barobar kamaytiradi.
Race condition — bir nechta oqimning yozish bilan umumiy ma'lumotlarga bir vaqtda kirishi paytida oldindan aytib bo'lmaydigan xatti-harakat. Undan uch usulda qochish mumkin: atomik turlardan (AtomicInteger), blokirovkalardan (synchronized, NSLock) foydalanish yoki kirishni navbat orqali (DispatchQueue serial, Kotlin-da Actor) serializatsiya qilish. Eng yaxshi amaliyot — umumiy mutable holatni minimallashtirish va immutability ishlatish.
Thread — ~1 MB stek egallaydigan va operatsion tizim yadrosiga bog'langan nativ tizim obyekti. Korutin — muayyan oqimga bog'lanmagan va bloklashsiz to'xtatilishi mumkin bo'lgan (suspend) yengil Kotlin bajarish birligi. Bitta oqim minglab korutinlarni bajarishi mumkin. Korutinlar xotira jihatidan samaraliroq va callbacksiz asinxron kod yozish imkonini beradi.
Deadlock ANR bo'lmasdan ilovaning to'liq muzlab qolishi sifatida namoyon bo'ladi. Android-da barcha oqimlarning steklarini dump qilish uchun Thread.getAllStackTraces() dan foydalaning — ikkita oqim bir-birining blokirovkasini kutadi. iOS-da — Thread.callStackSymbols. Vositalar: Android Studio Profiler (Threads tab), Instruments (iOS, Thread State View). Oldini olish: blokirovkalarni qat'iy tartibda oling, time-out bilan tryLock ishlating.
Xulosalar
Biz kalit topshirig'i bilan mobil ilovani ishlab chiqamiz
IT Sectr 2017-yildan beri startaplar va korxonalar uchun iOS va Android ilovalarini yaratadi. Biz sizga maslahat beramiz va eng yaxshi yechimni taklif qilamiz.