Starvation sa mga mobile application — esensya, mga sanhi, at mga paraan upang maiwasan ang gutom ng thread

May-akda: IT Sectr Nai-publish: 2026-03-18 Oras ng pagbabasa: 10 min

Starvation (gutom ng thread) — ay isang sitwasyon kung saan ang isang thread ay hindi makakuha ng access sa resource na kinakailangan upang ipagpatuloy ang trabaho, kahit na ito ay handa na para sa pagpapatupad. Ayon sa Baeldung (Java Thread Starvation, 2024), ang gutom ay nangyayari dahil sa hindi patas na pag-iiskedyul, kapag ang mga thread na may mababang priyoridad ay patuloy na ipinagpapaliban pabor sa mga may mas mataas na priyoridad. Hindi tulad ng Deadlock, Starvation ay hindi hinaharangan ang thread — nananatili ito sa estado na RUNNABLE, ngunit hindi kailanman nakakakuha ng oras ng processor.

Mga pangunahing punto

  • Starvation — sitwasyon kung saan ang isang thread ay hindi makakuha ng access sa resource, kahit na handa itong isagawa
  • Hindi tulad ng Deadlock, ang thread sa panahon ng gutom ay nananatili sa estado na RUNNABLE — hindi naka-block, ngunit hindi umuunlad
  • Hindi patas na pag-iiskedyul (halimbawa, pag-synchronize sa pamamagitan ng synchronized) — pangunahing sanhi ng Starvation sa JVM
  • Fair Lock (ReentrantLock(true)) ginagarantiyahan ang patas na pagkakasunod-sunod ng access sa lock sa pagkakasunud-sunod ng pila
  • Thread Priority sa mobile development ay inirerekomenda na huwag baguhin — Android Runtime mismo ang namamahala ng mga priyoridad

Ano ang Starvation?

Starvation (gutom ng thread) — ay isang problema ng multi-thread programming kung saan ang isang thread ay hindi makakuha ng access sa resource na kinakailangan upang maisagawa ang gawain, kahit na ang resource ay hindi permanenteng naka-block ng ibang thread. Ang thread ay nasa estado na RUNNABLE, ngunit ang scheduler o mekanismo ng pag-synchronize ay sistematikong ipinagpapaliban ang pagpapatupad nito pabor sa ibang mga thread.

Sa mobile development, ang Starvation ay nagpapakita bilang hindi pantay na pagpapatupad ng mga gawain: ang ilang operasyon ay isinasagawa kaagad, ang iba — may mga sakunang pagkaantala. Halimbawa, ang background thread na nag-si-sync ng data ay maaaring hindi kailanman makakuha ng access sa database kung ang UI thread at animation handlers ay patuloy itong nauunahan. Ayon sa Android Developer Blog (Performance Matters, 2023), humigit-kumulang 12% ng mga kaso ng mga nawawalang frame (jank) sa Android ay sanhi ng Starvation ng mga background task kung saan nakadepende ang rendering.

Ang pangunahing pagkakaiba ng Starvation sa Deadlock — reversibility. Kung ang load ng system ay bumaba o ang mga priyoridad ay muling ipinamahagi, ang gutom na thread ay maaaring makakuha ng resource at matapos ang trabaho. Gayunpaman, sa mga kondisyon ng patuloy na mataas na load, ang Starvation ay maaaring tumagal nang walang katiyakan, na lumilikha ng impresyon ng isang naka-freeze na application.

Mga sanhi ng gutom ng thread

Hindi patas na mga lock (Non-Fair Locks)

synchronized sa Java at Kotlin — klasikong halimbawa ng isang hindi patas na mekanismo. Sa mataas na kompetisyon, ang JVM ay maaaring walang katapusang magbigay ng lock sa parehong mga aktibong thread, habang ang ibang mga thread ay patuloy na natatalo sa karera. Ito ay hindi bug ng JVM, kundi isang feature ng implementasyon: ang mga hindi patas na lock ay nagbibigay ng mas mataas na throughput sa kapinsalaan ng pagkakapantay-pantay ng access. Para sa mga mobile application na may 4-8 thread, ang problemang ito ay partikular na nauugnay.

Maling paggamit ng mga priyoridad

Ang pagtatakda ng iba't ibang priyoridad ng thread ay maaaring humantong sa Starvation ng mga thread na may mababang priyoridad. Sa Android Runtime, ang CFS (Completely Fair Scheduler) scheduler ng Linux ay namamahagi ng oras ng processor nang proporsyonal sa mga priyoridad, at kung ang mga thread na may mataas na priyoridad ay patuloy na aktibo, ang mga thread na may mababang priyoridad ay maaaring hindi kailanman makakuha ng CPU. Mahigpit na hindi inirerekomenda ng Google na baguhin ang mga priyoridad ng thread sa Android — ang system mismo ang namamahala sa mga ito.

Mahahabang critical section

Kung ang isang thread ay humahawak ng lock nang napakatagal (nagsasagawa ng mabibigat na kalkulasyon, network request, o file operations sa loob ng synchronized block), ang ibang mga thread na naghihintay sa lock na ito ay nagugutom. Ito ay lalong mapanganib sa Android, kung saan ang mahahabang operasyon sa UI thread ay nagdudulot ng ANR, at ang paglipat ng mga ito sa background thread nang hindi ino-optimize ang critical section ay naglilipat ng problema ng Starvation sa mga worker thread.

Halimbawa ng Starvation sa Kotlin code

Isaalang-alang natin ang isang halimbawa kung saan ang isang thread ay masyadong madalas na kumukuha ng lock dahil sa hindi patas na pag-iiskedyul. Ang Starvation ay ipinapakita sa pamamagitan ng walang katapusang loop ng isang thread na may mataas na priyoridad, na hindi pinapayagan ang thread na may mababang priyoridad na ma-access ang shared resource.

kotlin
class SharedResource {
    private val lock = Any()

    fun criticalSection(id: String) {
        synchronized(lock) {
            println("$id nakakuha ng access")
            Thread.sleep(10)  // simulasyon ng trabaho
        }
    }
}

fun main() {
    val resource = SharedResource()

    // Thread na may mataas na priyoridad — patuloy na aktibo
    val highPriority = Thread {
        while (true) {
            resource.criticalSection("High")
        }
    }

    // Thread na may mababang priyoridad — maaaring hindi kailanman makakuha ng access
    val lowPriority = Thread {
        while (true) {
            resource.criticalSection("Low")
        }
    }

    highPriority.start()
    lowPriority.start()
    // "Low" ay maaaring hindi kailanman mag-print ng mensahe — Starvation!
}

Sa halimbawang ito, ang highPriority thread ay patuloy na kumukuha ng lock at inilalabas ito nang 10 ms lamang. Dahil sa hindi patas na katangian ng synchronized, ang JVM scheduler ay may mataas na posibilidad na ibigay muli ang lock sa parehong thread na kakatapos lamang maglabas nito — ang thread na may mababang priyoridad ay nagugutom. Ang solusyon — gamitin ang ReentrantLock(true) na may fair flag, na ginagarantiyahan ang pagkakasunud-sunod sa waiting queue.

Ang naitama na bersyon na may fair lock ay nagsisiguro ng patas na pamamahagi ng access sa resource.

kotlin
class FairSharedResource {
    private val fairLock = ReentrantLock(true)  // fair = true

    fun criticalSection(id: String) {
        fairLock.lock()
        try {
            println("$id nakakuha ng access (fair)")
            Thread.sleep(10)
        } finally {
            fairLock.unlock()
        }
    }
}

Starvation vs Deadlock vs Livelock

Tatlong klasikong problema ng multi-threading — Starvation, Deadlock at Livelock — ay madalas na pinagsama, ngunit ang kanilang mga mekanismo at paraan ng pag-aalis ay magkakaiba. Starvation — handa ang thread ngunit hindi nakakakuha ng resource. Deadlock — ang mga thread ay naka-block ng cyclic waiting. Livelock — ang mga thread ay aktibo ngunit hindi umuunlad.

ParameterStarvationDeadlockLivelock
Estado ng threadRUNNABLEBLOCKEDRUNNABLE
Pag-unladWalaWalaWala (kahit aktibo)
Pagkonsumo ng CPUMababaMinimalMataas (hanggang 100%)
SanhiHindi patas na pag-iiskedyulCyclic waitingParehong reaksyon sa conflict
Pangunahing solusyonFair Lock, pagbawas ng critical sectionHierarchy ng mga lockLimit sa pagsubok muli, exponential backoff

Ang Starvation ay itinuturing na hindi gaanong kritikal kaysa Deadlock, dahil hindi ito nakamamatay — kapag bumaba ang load, ang gutom na thread ay sa huli ay isasagawa. Gayunpaman, sa mga kondisyon ng tunay na paggamit ng Android application, kung saan limitado ang memory at CPU, ang Starvation ay maaaring tumagal nang ilang minuto, na lumilikha ng hindi katanggap-tanggap na UX.

Paano matukoy ang Starvation

Thread Dump na may paulit-ulit na pagkuha sa maikling pagitan — ang pangunahing paraan ng pagtuklas ng gutom. Kung ang isang thread ay patuloy na nasa estado na RUNNABLE, ngunit ang call stack nito ay hindi nagbabago sa kabuuan ng ilang dump — ito ay isang klasikong senyales ng Starvation. Sa Android Studio, ginagamit ang Android Profiler na may pag-record ng estado ng thread sa paglipas ng panahon.

Ang automated detection ay posible sa pamamagitan ng pag-monitor ng oras ng pagpapatupad ng mga gawain. Kung ang isang gawain na may predictable execution time (halimbawa, 50 ms) ay isinasagawa nang 5 segundo o higit pa — may mataas na posibilidad ng Starvation. Sa mga mobile application, ang Firebase Performance Monitoring ay nagbibigay-daan sa pag-configure ng custom traces para sa mga critical section at pagtanggap ng mga notification kapag lumampas sa threshold values.

Para sa diagnosis ng Starvation dahil sa synchronized blocks, gamitin ang Java Flight Recorder (JFR) (available sa Android sa pamamagitan ng OpenJDK API) o Async Profiler. Ang mga tool na ito ay nagpapakita kung aling mga monitor ang may pinakamahabang oras ng paghihintay at kung aling mga thread ang nakikipagkumpitensya para sa bawat monitor. Ang data ng JFR ay isinasama sa IntelliJ IDEA Ultimate sa pamamagitan ng built-in na profiler.

Mga paraan upang maiwasan ang gutom ng thread

Fair Lock (ReentrantLock na may true flag)

ReentrantLock(true) ginagarantiyahan na ang mga thread ay tumatanggap ng lock sa pagkakasunud-sunod ng pila (FIFO). Hindi tulad ng synchronized, hindi pinapayagan ng fair lock ang sitwasyon kung saan ang thread na kakatapos lang maglabas ng lock ay agad itong kukunin muli. Ito ay ganap na nag-aalis ng Starvation, kahit na binabawasan ang pangkalahatang throughput ng 10-20% dahil sa overhead ng pagpapanatili ng pila.

Atomic structures na walang lock

Lock-free data structures (ConcurrentHashMap, AtomicReference, LongAdder) ay nag-aalis ng Starvation sa pamamagitan ng depinisyon, dahil hindi ito naglalaman ng mga lock na maaaring hawakan ng isang thread. Lahat ng operasyon ay gumagamit ng CAS instructions ng processor, na ginagarantiyahan ang pag-unlad ng kahit isang thread sa isang tiyak na bilang ng mga hakbang. Para sa mobile development, piliin ang ConcurrentLinkedQueue para sa mga task queue.

Maikling critical section

Pag-minimize ng oras ng paghawak ng lock — isang unibersal na paraan upang mabawasan ang panganib ng Starvation. Ilabas ang mabibigat na operasyon (network, disk I/O, kumplikadong kalkulasyon) sa labas ng synchronized block. Gamitin ang ReadWriteLock para sa mga scenario kung saan ang mga reader ay hindi dapat magutom dahil sa mga bihirang writer. Ang Kotlin Coroutines library ay nagbibigay ng Mutex na may suspending mechanism na hindi humaharang sa OS thread.

Condition variable at signal

Condition.await() at signal() ay dapat gamitin nang may pag-iingat: ang thread na naghihintay sa Condition ay nagigising kasama ng ibang mga thread (spurious wakeup) at lahat sila ay nakikipagkumpitensya para sa lock. Kung ang isang thread pagkatapos ng await ay agad na bumalik sa paghihintay, at ang iba ay namamahala na makuha ang lock — ang gutom na thread ay maaaring gumising at matulog nang walang katapusan. Palaging suriin ang condition sa isang while loop, hindi sa if, upang garantiyahan ang muling pagsusuri.

Mga madalas itanong

Ano ang pagkakaiba ng Starvation at Priority Inversion?

Priority Inversion — ay isang sitwasyon kung saan ang isang thread na may mababang priyoridad ay humahawak ng lock na kinakailangan ng isang thread na may mataas na priyoridad. Bilang resulta, ang thread na may mataas na priyoridad ay naghihintay sa thread na may mababang priyoridad — ang mga priyoridad ay nababaligtad. Ang Starvation ay isang mas malawak na problema: ang thread ay hindi nakakakuha ng resource anuman ang priyoridad, dahil sa hindi patas na pag-iiskedyul o mahabang critical section.

Maaari bang mangyari ang Starvation sa isang single-thread application?

Hindi, ang Starvation — ay isang problema ng multi-threading. Sa single-thread code, walang kompetisyon para sa resources at thread scheduling. Gayunpaman, ang Starvation ay maaaring mangyari sa asynchronous single-thread code (halimbawa, JavaScript event loop), kung ang isang micro-task ay walang katapusang ipinagpapaliban ang pagpapatupad ng iba sa pamamagitan ng setTimeout na may zero delay.

Paano nauugnay ang Java Memory Model sa Starvation?

JMM (Java Memory Model) ay tumutukoy sa mga panuntunan ng visibility ng mga pagbabago sa pagitan ng mga thread, ngunit hindi ginagarantiyahan ang patas na pag-iiskedyul. Ang synchronized alinsunod sa JMM ay nagbibigay ng sequential consistency — pangunahing kawastuhan — ngunit hindi pumipigil sa Starvation. Para sa pagiging patas, kinakailangan ang mga karagdagang mekanismo na hindi bahagi ng JMM specification.

Ano ang Starvation sa Android UI thread?

UI thread (Main Thread) ay hindi maaaring magutom sa klasikong kahulugan, dahil ito ay may pinakamataas na priyoridad. Gayunpaman, ang Starvation ay nangyayari kapag ang UI thread ay naghihintay ng resulta mula sa isang gutom na background thread. Karaniwang scenario: Ang AsyncTask o coroutine ay naglo-load ng data, ngunit hindi ma-access ang database dahil sa kompetisyon sa ibang mga thread, at ang UI ay nag-freeze sa paghihintay.

Paano maiwasan ang Starvation sa Kotlin Coroutines?

Sa coroutines, upang maiwasan ang Starvation gamitin ang limitedParallelism sa Dispatchers.IO upang maiwasan ang pagkaubos ng thread. Para sa pag-synchronize, ilapat ang Mutex mula sa kotlinx.coroutines.sync — nito isinasuspinde ang coroutine, hindi hinaharangan ang thread, na nagbabawas ng panganib ng gutom. Iwasan ang runBlocking sa coroutines, dahil maaari itong kumuha ng pool thread at magdulot ng Starvation ng ibang coroutines.

Buod

  • Starvation — sitwasyon kung saan ang thread ay handa para sa pagpapatupad ngunit hindi nakakakuha ng resource dahil sa hindi patas na pag-iiskedyul
  • Hindi tulad ng Deadlock, sa gutom ang thread ay nasa RUNNABLE state at maaaring isagawa kapag bumaba ang load
  • Hindi patas na mga lock (synchronized) at maling paggamit ng mga priyoridad — pangunahing sanhi ng Starvation
  • Fair Lock (ReentrantLock na may true flag) ginagarantiyahan ang FIFO access order at ganap na nag-aalis ng gutom
  • Lock-free structures (ConcurrentHashMap, AtomicReference) nag-aalis ng Starvation sa antas ng arkitektura
  • Thread Dump na may paulit-ulit na pagkuha at Java Flight Recorder — mabisang paraan ng diagnosis ng Starvation
  • Maikling critical section at ReadWriteLock nagbabawas ng posibilidad ng gutom sa mga system na may mataas na load

Gagawa kami ng mobile application na turnkey

Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.

Pag-usapan ang proyekto

Basahin din