Starvation (thread ochligi) — bu thread ishni davom ettirish uchun zarur bo'lgan resursga kirish ololmaydigan, garchi u o'zi bajarishga tayyor bo'lgan vaziyatdir. Baeldung (Java Thread Starvation, 2024) ga ko'ra, ochlik adolatsiz rejalashtirish tufayli yuzaga keladi, past prioritetli threadlar doimiy ravishda yuqori prioritetlilar foydasiga kechiktiriladi. Deadlockdan farqli o'laroq, Starvation threadni bloklamaydi — u RUNNABLE holatida qoladi, lekin hech qachon protsessor vaqtini olmaydi.
Asosiy ma'lumotlar
Starvation (thread ochligi) — bu ko'p threadli dasturlash muammosi bo'lib, unda thread vazifani bajarish uchun zarur resursga kirish ololmaydi, garchi resurs boshqa thread tomonidan abadiy bloklanmagan bo'lsa. Thread RUNNABLE holatidadir, lekin rejalashtiruvchi yoki sinxronizatsiya mexanizmi uning bajarilishini boshqa threadlar foydasiga muntazam ravishda kechiktiradi.
Mobil ishlanmada Starvation notekis vazifa bajarilishi sifatida namoyon bo'ladi: ba'zi operatsiyalar bir zumda bajariladi, boshqalari — halokatli kechikishlar bilan. Masalan, ma'lumotlarni sinxronlashtiradigan fon threadi, UI thread va animatsiya ishlovchilari uni doimiy ravishda ortda qoldirsa, ma'lumotlar bazasiga kirish ololmasligi mumkin. Android Developer Blog (Performance Matters, 2023) ma'lumotlariga ko'ra, Androidda o'tkazib yuborilgan kadrlarning (jank) taxminan 12% i renderlash bog'liq bo'lgan fon vazifalarining Starvationi tufayli yuzaga keladi.
Starvationning Deadlockdan asosiy farqi — qaytariluvchanlik. Agar tizim yuki kamaysa yoki prioritetlar qayta taqsimlansa, ochlik chekayotgan thread resursni olib ishni tugatishi mumkin. Biroq doimiy yuqori yuk sharoitida Starvation cheksiz davom etishi mumkin, muzlatilgan ilova taassurotini yaratadi.
Java va Kotlinda synchronized — adolatsiz mexanizmning klassik namunasidir. Yuqori raqobatda JVM bir xil faol threadlarga qulfni cheksiz berishi mumkin, boshqa threadlar esa doimo poygada yutqazadi. Bu JVM xatosi emas, balki amalga oshirish xususiyatidir: adolatsiz qulflar kirishning bir xilligi hisobiga yuqori o'tkazish qobiliyatini ta'minlaydi. 4-8 threadli mobil ilovalar uchun bu muammo ayniqsa dolzarbdir.
Turli thread prioritetlarini o'rnatish past prioritetli threadlarning Starvationiga olib kelishi mumkin. Android Runtime da Linuxning CFS (Completely Fair Scheduler) rejalashtiruvchisi protsessor vaqtini prioritetlarga mutanosib ravishda taqsimlaydi va agar yuqori prioritetli threadlar doimiy faol bo'lsa, past prioritetlilar hech qachon CPU ololmasligi mumkin. Google Androidda thread prioritetlarini o'zgartirishni qat'iy tavsiya etmaydi — tizim ularni o'zi boshqaradi.
Agar thread qulfni juda uzoq ushlab tursa (synchronized bloki ichida og'ir hisoblashlar, tarmoq so'rovlari yoki fayl operatsiyalarini bajarilsa), bu qulfni kutayotgan boshqa threadlar ochlik chekadi. Bu ayniqsa Androidda xavfli, bu yerda UI threaddagi uzoq operatsiyalar ANRga sabab bo'ladi va ularni fon threadlariga o'tkazish kritik seksiyalarni optimallashtirmasdan Starvation muammosini ishchi threadlarga o'tkazadi.
Bir misolni ko'rib chiqaylik, unda bir thread adolatsiz rejalashtirish tufayli qulfni juda tez-tez egallab oladi. Starvation yuqori prioritetli threadning cheksiz sikl orqali namoyish etiladi, bu past prioritetli threadga umumiy resursga kirishga imkon bermaydi.
class SharedResource {
private val lock = Any()
fun criticalSection(id: String) {
synchronized(lock) {
println("$id kirish oldi")
Thread.sleep(10) // ish simulyatsiyasi
}
}
}
fun main() {
val resource = SharedResource()
// Yuqori prioritetli thread — doimiy faol
val highPriority = Thread {
while (true) {
resource.criticalSection("High")
}
}
// Past prioritetli thread — hech qachon kirish ololmasligi mumkin
val lowPriority = Thread {
while (true) {
resource.criticalSection("Low")
}
}
highPriority.start()
lowPriority.start()
// "Low" hech qachon xabarni chiqarmasligi mumkin — Starvation!
}
Bu misolda highPriority threadi doimiy ravishda qulfni egallab oladi va uni faqat 10 msga qo'yib beradi. synchronizedning adolatsiz xususiyati tufayli JVM rejalashtiruvchisi yuqori ehtimollik bilan qulfni yana o'sha threadga beradi — past prioritetli thread ochlik chekadi. Yechim — fair flagi bilan ReentrantLock(true) dan foydalanish, bu kutish navbatida tartibni kafolatlaydi.
Fair lock bilan tuzatilgan versiya resursga kirishning adolatli taqsimlanishini ta'minlaydi.
class FairSharedResource {
private val fairLock = ReentrantLock(true) // fair = true
fun criticalSection(id: String) {
fairLock.lock()
try {
println("$id kirish oldi (fair)")
Thread.sleep(10)
} finally {
fairLock.unlock()
}
}
}
Uch klassik ko'p threadlilik muammosi — Starvation, Deadlock va Livelock — ko'pincha birlashtiriladi, lekin ularning mexanizmlari va bartaraf etish usullari turlicha. Starvation — thread tayyor, lekin resurs olmaydi. Deadlock — threadlar siklik kutish bilan bloklangan. Livelock — threadlar faol, lekin rivojlanmaydi.
| Parametr | Starvation | Deadlock | Livelock |
|---|---|---|---|
| Thread holati | RUNNABLE | BLOCKED | RUNNABLE |
| Rivojlanish | Yo'q | Yo'q | Yo'q (faol bo'lsa ham) |
| CPU iste'moli | Past | Minimal | Yuqori (100% gacha) |
| Sabab | Adolatsiz rejalashtirish | Siklik kutish | Mojaroga bir xil reaksiya |
| Asosiy yechim | Fair Lock, kritik seksiyalarni kamaytirish | Qulf ierarxiyasi | Qayta urinish limiti, eksponensial backoff |
Starvation Deadlockdan kamroq kritik hisoblanadi, chunki u halokatli emas — yuk kamayganda ochlik chekayotgan thread baribir bajariladi. Biroq xotira va CPU cheklangan Android ilovalarining real foydalanish sharoitida Starvation daqiqalar davom etishi mumkin, qabul qilib bo'lmaydigan UX yaratadi.
Thread Dump qisqa intervallarda takroriy olish bilan — ochlikni aniqlashning asosiy usuli. Agar thread izchil RUNNABLE holatida bo'lsa, lekin uning chaqiruv steklari bir nechta dump davomida o'zgarmasa — bu Starvationning klassik belgisidir. Android Studio da buning uchun vaqt davomida threadlar holatini yozib oluvchi Android Profiler ishlatiladi.
Avtomatlashtirilgan aniqlash vazifalarning bajarilish vaqtini kuzatish orqali mumkin. Agar bashorat qilinadigan bajarilish vaqti (masalan, 50 ms) bo'lgan vazifa 5 soniya yoki undan ko'proq bajarilsa — Starvation ehtimoli yuqori. Mobil ilovalarda Firebase Performance Monitoring kritik seksiyalar uchun maxsus izlarni (custom traces) sozlash va chegara qiymatlari oshib ketganda bildirishnomalar olish imkonini beradi.
synchronized bloklari sababli Starvation diagnostikasi uchun Java Flight Recorder (JFR) (Androidda OpenJDK API orqali mavjud) yoki Async Profiler dan foydalaning. Bu vositalar qaysi monitorlar eng uzoq kutish vaqtiga ega ekanligini va qaysi threadlar har bir monitor uchun raqobatlashayotganini ko'rsatadi. JFR ma'lumotlari IntelliJ IDEA Ultimate bilan o'rnatilgan profiler orqali integratsiyalanadi.
ReentrantLock(true) threadlar qulfni navbat tartibida (FIFO) olishini kafolatlaydi. synchronizeddan farqli o'laroq, fair lock qulfni qo'yib yuborgan threadning darhol uni qayta egallashiga yo'l qo'ymaydi. Bu Starvationni butunlay yo'q qiladi, garchi navbatni saqlash uchun qo'shimcha xarajatlar tufayli umumiy o'tkazish qobiliyatini 10-20% kamaytirsa ham.
Lock-free ma'lumot strukturalari (ConcurrentHashMap, AtomicReference, LongAdder) ta'rifi bo'yicha Starvationni istisno qiladi, chunki ularda bir thread tomonidan ushlab turiladigan qulflar yo'q. Barcha operatsiyalar protsessorning CAS ko'rsatmalaridan foydalanadi, bu cheklangan qadamlarda kamida bitta threadning rivojlanishini kafolatlaydi. Mobil ishlanma uchun vazifa navbatlari uchun ConcurrentLinkedQueue ni afzal ko'ring.
Qulfni ushlab turish vaqtini minimallashtirish — Starvation xavfini kamaytirishning universal usuli. Og'ir operatsiyalarni (tarmoq, disk kiritish-chiqarish, murakkab hisoblashlar) synchronized blokidan tashqariga chiqaring. O'quvchilar kamdan-kam yozuvchilar tufayli ochlik chekmasligi uchun ReadWriteLock dan foydalaning. Kotlin Coroutines kutubxonasi OS threadini bloklamaydigan suspending mexanizmi bilan Mutexni taqdim etadi.
Condition.await() va signal() ehtiyotkorlik bilan ishlatilishi kerak: Conditionda kutayotgan thread boshqa threadlar bilan birga uyg'onadi (spurious wakeup) va hammasi qulf uchun raqobatlashadi. Agar bir thread awaitdan so'ng darhol kutishga qaytsa, boshqalar esa qulfni egallashga ulgursa — ochlik chekayotgan thread cheksiz uyg'onib uxlab qolishi mumkin. Shartni har doim while siklida tekshiring, ifda emas, qayta tekshirishni kafolatlash uchun.
Tez-tez so'raladigan savollar
Priority Inversion — bu past prioritetli thread yuqori prioritetli thread uchun zarur bo'lgan qulfni ushlab turgan vaziyat. Natijada yuqori prioritetli thread past prioritetlini kutadi — prioritetlar teskari bo'ladi. Starvation kengroq muammo: thread prioritetdan qat'iy nazar, adolatsiz rejalashtirish yoki uzoq kritik seksiyalar tufayli resurs olmaydi.
Yo'q, Starvation — ko'p threadlilik muammosi. Bir threadli kodda resurslar uchun raqobat va thread rejalashtirishi mavjud emas. Biroq Starvation asinxron bir threadli kodda (masalan, JavaScript event loop) yuzaga kelishi mumkin, agar bir mikro vazifa setTimeout orqali nol kechikish bilan boshqalarning bajarilishini cheksiz kechiktirsa.
JMM (Java Memory Model) threadlar o'rtasida o'zgarishlarning ko'rinishi qoidalarini belgilaydi, lekin adolatli rejalashtirishni kafolatlamaydi. JMMga muvofiq synchronized sequential consistency — asosiy to'g'rilikni ta'minlaydi, lekin Starvationning oldini olmaydi. Adolat uchun JMM spetsifikatsiyasiga kirmagan qo'shimcha mexanizmlar talab qilinadi.
UI thread (Main Thread) klassik ma'noda ochlik cheka olmaydi, chunki u eng yuqori prioritetga ega. Biroq Starvation UI thread ochlik chekayotgan fon threadining natijasini kutganda yuzaga keladi. Odatdagi ssenariy: AsyncTask yoki korutin ma'lumotlarni yuklaydi, lekin boshqa threadlar bilan raqobat tufayli ma'lumotlar bazasiga kirish ololmaydi va UI kutishda muzlaydi.
Korutinlarda Starvationning oldini olish uchun Dispatchers.IO da limitedParallelism dan foydalaning, threadlarning tugashiga yo'l qo'ymaslik uchun. Sinxronizatsiya uchun kotlinx.coroutines.sync dan Mutex ni qo'llang — u korutinni to'xtatadi, threadni bloklamaydi, bu ochlik xavfini kamaytiradi. Korutinlarda runBlocking dan saqlaning, chunki u pool threadini egallab boshqa korutinlarning Starvationiga sabab bo'lishi mumkin.
Xulosa
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.