Deadlock sa Mobile Development: Ano Ito, Mga Sanhi ng Paglitaw at Mga Paraan upang Maiwasan ang Mutual Blocking

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

Deadlock (mutual blocking) — ay isang estado kung saan dalawa o higit pang mga thread ang walang katapusang naghihintay ng pagpapalaya ng mga mapagkukunang kinuha ng ibang mga kalahok. Ayon sa Oracle Java Tutorials (2024), ang Deadlock ay nangyayari sa paikot na paghihintay, kapag ang bawat thread ay may hawak na lock na kailangan ng isa pang thread. Kung walang espesyal na paraan ng pagtuklas, Deadlock ang ganap na humihinto sa pagpapatakbo ng application nang walang nakikitang mga error.

Mga Pangunahing Punto

  • Deadlock — mutual blocking ng mga thread, kung saan ang bawat isa ay naghihintay ng mapagkukunang inookupahan ng isa pang thread
  • Apat na kondisyon Coffman (Mutual Exclusion, Hold and Wait, No Preemption, Circular Wait) ay kinakailangan para sa paglitaw ng Deadlock
  • Deadlock ay naiiba sa Starvation dahil ang mga thread ay hindi naka-block, ngunit aktibong naghihintay sa paikot na dependency
  • Thread Dump — pangunahing kasangkapan para sa pagtuklas ng Deadlock sa JVM at Android Runtime
  • Hierarkiya ng mga lock at pinag-isang pagkakasunod-sunod ng pagkuha ng mapagkukunan — pangunahing paraan upang maiwasan ang mutual blocking

Ano ang Deadlock?

Deadlock (mutual blocking) — ay isang sitwasyon sa multi-thread programming kung saan dalawa o higit pang mga thread ang permanenteng nagba-block sa isa’t isa. Ang bawat thread ay may hawak na mapagkukunang kailangan ng isa pang thread at hindi ito pinapalaya, naghihintay na makuha ang nawawalang mapagkukunan. Bilang resulta, wala sa mga thread ang maaaring magpatuloy sa pagpapatakbo.

Sa mobile development, ang Deadlock ay partikular na kritikal dahil hindi ito nagdudulot ng mga exception o crash. Ang application ay humihinto na lamang sa pagtugon sa mga aksyon ng gumagamit (ANR — Application Not Responding), at ang tanging paraan ay sapilitang pagtatapos ng proseso. Ayon sa data ng Google (Android Performance Patterns, 2023), humigit-kumulang 15% ng mga ulat ng ANR sa Google Play Console ay nauugnay sa mutual blocking sa mga background thread.

Ang pangunahing pagkakaiba ng Deadlock mula sa iba pang mga problema ng concurrency — ang hindi pagbabalik nito nang walang panlabas na interbensyon. Ang mga thread ay hindi magpapalaya ng mga mapagkukunan nang mag-isa, dahil ang scheduler ng operating system ay hindi maaaring sapilitang bawiin ang lock. Ito ang nagpapaiba ng Deadlock sa Livelock, kung saan ang mga thread ay aktibo ngunit hindi gumagawa ng kapaki-pakinabang na trabaho.

Mga Kondisyon ng Paglitaw ng Deadlock

Noong 1971, si Edward G. Coffman ay nagbalangkas ng apat na sapilitang kondisyon na kinakailangan para sa paglitaw ng Deadlock. Kung kahit isa sa mga ito ay wala, ang mutual blocking ay imposible. Ang mga kondisyong ito ay kilala bilang mga kondisyon ng Coffman at nasa puso ng lahat ng algorithm ng pag-iwas sa Deadlock.

Mutual Exclusion

Ang mapagkukunan ay maaaring makuha lamang ng isang thread sa bawat sandali. Kung pinapayagan ng mapagkukunan ang sabay-sabay na pagbasa ng maraming thread (halimbawa, ReadWriteLock sa mode ng pagbasa), hindi nangyayari ang Deadlock. Ang kondisyong ito ay nagmumula sa mismong kalikasan ng Mutex at mga lock.

Hold and Wait

Ang isang thread ay may hawak na nakuhang mapagkukunan at sabay na naghihintay ng pagkuha ng isa pang mapagkukunan. Kung ang thread ay maaaring magpalaya sa kasalukuyang mapagkukunan bago humiling ng susunod (sa pamamagitan ng two-phase locking), ang kondisyon ng Hold and Wait ay nilalabag. Sa Android, ito ay madalas na nagpapakita kapag ang isang thread ay may hawak na database lock at sinusubukang makuha ang SharedPreferences lock.

No Preemption

Ang operating system ay hindi maaaring sapilitang tanggalin ang lock mula sa thread. Ang mapagkukunan ay pinapalaya lamang kapag ang thread mismo ang naglabas nito. Sa ilang mga sistema (halimbawa, SQLite WAL mode), ang sapilitang preemption ay ipinatutupad sa antas ng mga indibidwal na operasyon, na nagbabawas ng panganib ng Deadlock.

Circular Wait

Mayroong saradong kadena ng mga thread, bawat isa ay naghihintay ng mapagkukunang hawak ng susunod sa kadena. Halimbawa, ang thread A ay may hawak na mapagkukunan 1 at naghihintay ng mapagkukunan 2, ang thread B ay may hawak na mapagkukunan 2 at naghihintay ng mapagkukunan 1. Ito ang tanging kondisyon na maaaring alisin ng developer sa arkitektural na paraan — sa pamamagitan ng hierarkiya ng mga lock. Kung lahat ng thread ay kumukuha ng mga mapagkukunan sa isang mahigpit na itinakdang pandaigdigang pagkakasunud-sunod, ang cycle ay pisikal na imposible.

Sa pagsasagawa, sa mga Android application, ang Deadlock ay kadalasang nangyayari dahil sa implicit na pagtawid ng mga lock ng iba’t ibang antas: database lock (Room), SharedPreferences lock, at memory collection lock. Ang bawat isa sa mga lock na ito ay pinamamahalaan ng iba’t ibang mga component, at kung walang sentralisadong protocol ng pagkakasunud-sunod ng pagkuha, ang mga developer ay hindi sinasadyang lumikha ng mga cycle.

Halimbawa ng Deadlock sa Kotlin Code

Tingnan natin ang isang klasikong halimbawa ng mutual blocking — dalawang thread ang kumukuha ng mga lock sa magkaibang pagkakasunud-sunod. Kung ang unang thread ay nag-block ng mapagkukunan A at sumusubok na makuha ang B, at ang pangalawa ay nag-block ng B at sumusubok na makuha ang A, nangyayari ang Deadlock.

kotlin
class DeadlockExample {
    private val lockA = Any()
    private val lockB = Any()

    fun operationA() {
        synchronized(lockA) {
            Thread.sleep(50)  // simulasyon ng trabaho
            synchronized(lockB) {
                println("operationA nakumpleto")
            }
        }
    }

    fun operationB() {
        synchronized(lockB) {  // baligtad na pagkakasunud-sunod!
            Thread.sleep(50)
            synchronized(lockA) {
                println("operationB nakumpleto")
            }
        }
    }
}

fun main() {
    val ex = DeadlockExample()
    Thread { ex.operationA() }.start()
    Thread { ex.operationB() }.start()
    // Ang application ay mag-freeze nang tuluyan — Deadlock!
}

Sa halimbawang ito, operationA ay kumukuha ng lockA, at operationB ay kumukuha ng lockB. Pagkatapos ang bawat isa ay sumusubok na makuha ang pangalawang lock — at parehong naghihintay nang walang katapusan. Ang programa ay nag-freeze nang walang mensahe ng error. Ang tanging paraan ng pag-aayos ay garantiya ang parehong pagkakasunud-sunod ng pagkuha ng lock sa lahat ng mga pamamaraan.

Deadlock vs Starvation vs Livelock

Ang tatlong problemang ito ng concurrency ay madalas na napagkakamalan, ngunit ang kanilang mga mekanismo at bunga ay panimula na naiiba. Deadlock — kumpletong pagtigil, Starvation — walang katapusang paghihintay para sa mapagkukunan, Livelock — aktibong kawalan ng gawa. Ang pag-unawa sa mga pagkakaiba ay kritikal para sa pagpili ng tamang estratehiya ng pag-aalis.

KatangianDeadlockStarvationLivelock
Estado ng mga threadNaka-block (BLOCKED)Handa (RUNNABLE)Aktibo (RUNNABLE)
Pagpapatupad ng trabahoHindiHindiOo, ngunit walang silbi
SanhiPaikot na paghihintayHindi patas na pag-iiskedyulMaling paghawak ng conflict
PagtuklasThread Dump, mga timeoutPagmomonitor ng progresoBilang ng mga pagsubok muli

Ang Starvation (gutom) ay nangyayari kapag ang scheduler ay patuloy na ipinagpapaliban ang pagpapatupad ng isang thread na may mababang priyoridad pabor sa iba. Hindi tulad ng Deadlock, ang thread ay hindi naka-block — ito ay handa nang isagawa ngunit hindi nakakakuha ng oras ng CPU. Sa Android, ang tipikal na senaryo ay isang background thread na may mababang priyoridad na hindi kailanman naisasagawa kung ang UI at Service thread ay patuloy na aktibo.

Livelock (aktibong lock) — sitwasyon kung saan ang mga thread ay hindi naka-block, ngunit walang katapusang tumutugon sa mga aksyon ng isa’t isa, nang hindi gumagawa ng kapaki-pakinabang na trabaho. Ang klasikong analohiya — dalawang tao ay nagkikita sa koridor at parehong sumusubok na magbigay-daan, gumagalaw sa parehong direksyon. Hindi tulad ng Deadlock, ang mga thread sa Livelock ay kumokonsumo ng CPU, nagpapababa ng baterya ng device.

Paano Matukoy ang Deadlock

Thread Dump (dump ng mga thread) — pangunahing kasangkapan para sa pagtuklas ng mutual blocking sa JVM at Android Runtime. Sa oras ng dump, awtomatikong sinusuri ng JVM ang dependency graph sa pagitan ng mga monitor at minamarkahan ang mga Deadlock cycle. Sa Android Studio, ang thread dump ay maaaring makuha sa pamamagitan ng Android Profiler o command na kill -3 PID mula sa ADB Shell.

Ang awtomatikong pagtuklas ng Deadlock sa runtime ay naisasagawa sa pamamagitan ng Watchdog timer. Kung ang isang thread ay hindi natapos ang operasyon sa loob ng itinakdang timeout, ang watchdog ay nagpapasimula ng paggawa ng dump at nagpapadala ng ulat sa Crash Reporting system (Firebase Crashlytics, Sentry). Ayon sa data ng Sentry (Issue Resolution Report, 2024), ang pagsasaayos ng watchdog ay nagbabawas ng oras ng diagnosis ng Deadlock mula sa mga linggo hanggang sa ilang oras.

Sa yugto ng pag-unlad, epektibo ang static analyzer na ThreadSafe mula sa JetBrains at Checker Framework na may Lock Checker module. Ang mga tool na ito ay sumusuri sa pagkakasunud-sunod ng pagkuha ng lock sa antas ng source code at nagbabala tungkol sa mga potensyal na cycle. Dagdag pa, inirerekomenda ang Test-Driven Deadlock Detection — mga stress test na nagpapatakbo ng mga operasyon na may iba’t ibang pagkakasunod-sunod ng pag-lock sa daan-daang thread.

Ang espesyal na atensyon ay nararapat sa Cooperative Deadlock Detection — pamamaraan kung saan ang mga thread ay nagpapalitan ng impormasyon tungkol sa mga nakuhang lock sa pamamagitan ng global registry. Kung ang isang thread ay nakakakita ng potensyal na cycle, pinapalaya nito ang lahat ng mapagkukunan at inuulit ang operasyon. Ang approach na ito ay ginagamit sa distributed systems (Apache ZooKeeper, Google Chubby) at unti-unting ipinakikilala sa mobile development sa pamamagitan ng mga library tulad ng Jetpack Sync.

Mga Paraan ng Pag-iwas sa Mutual Blocking

Hierarkiya ng mga Lock (Lock Ordering)

Ang pinaka-maaasahang paraan — magtatag ng pandaigdigang pagkakasunud-sunod ng pagkuha ng lock sa buong application. Kung lahat ng thread ay unang kumukuha ng lock na may mas maliit na numero, pagkatapos ay ang may mas malaking numero, ang paikot na paghihintay (kondisyon ng Circular Wait) ay imposible. Sa malalaking proyekto, ang pagkakasunud-sunod ay itinatakda sa dokumentasyon at nabe-verify sa pamamagitan ng code review.

TryLock na may Timeout

TryLock — paraan ng pag-lock na hindi humaharang sa thread nang walang katapusan, ngunit nagbabalik ng false kung ang lock ay hindi nakuha sa loob ng itinakdang oras. Sa Java, ito ay ipinatutupad sa pamamagitan ng ReentrantLock.tryLock(timeout, TimeUnit), sa Kotlin Coroutines — sa pamamagitan ng Mutex.withLock na may timeout. Kung nabigo, pinapalaya ng thread ang lahat ng nakuhang mapagkukunan at muling sumusubok mamaya.

Algorithm ng Banker (Banker’s Algorithm)

Algorithm ng Banker — teoretikal na paraan ng pag-iwas sa Deadlock na iminungkahi ni Edsger Dijkstra. Ito ay nagmomodelo ng pamamahagi ng mapagkukunan bilang mga transaksyon sa bangko: ang sistema ay hindi naglalaan ng mapagkukunan kung ito ay maaaring humantong sa isang hindi ligtas na estado (deadlock). Sa pagsasagawa, ang algorithm ay bihirang inilalapat sa mobile development dahil sa pagiging kumplikado ng paunang kaalaman sa maximum na pangangailangan ng mga thread, ngunit ang mga prinsipyo nito ay ginagamit sa SQLite database at file system.

Mga Madalas Itanong

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

Hindi, para sa mutual blocking ay kailangan ng hindi bababa sa dalawang thread. Sa single-thread code, lahat ng operasyon ay isinasagawa nang sunud-sunod, kaya ang paikot na paghihintay ay imposible. Gayunpaman, ang Deadlock ay maaaring mangyari sa pagitan ng mga proseso kapag gumagamit ng file lock o inter-process semaphore.

Paano naiiba ang Deadlock sa Kotlin Coroutines sa Deadlock sa mga thread?

Sa mga coroutine, ang Deadlock ay nangyayari sa antas ng suspend function at hindi hinaharangan ang OS thread, na ginagawang hindi gaanong kapansin-pansin. Ang Mutex mula sa kotlinx.coroutines ay suspending, hindi nito hinaharangan ang thread, ngunit ang coroutine ay hindi naisasagawa. Para sa pagtuklas, gamitin ang DebugProbes mula sa modyul na kotlinx-coroutines-debug.

Ano ang Deadlock sa SQLite sa Android?

SQLite Deadlock ay nangyayari kapag ang dalawang koneksyon sa database ay sumusubok na magsagawa ng mga transaksyon sa magkaibang pagkakasunud-sunod. Natutukoy ng SQLite ang mga ganitong sitwasyon at nagbabalik ng error code na SQLITE_BUSY o SQLITE_LOCKED. Sa Android, inirerekomenda ang paggamit ng Room na may iisang instance ng database at mga transaksyon sa pamamagitan ng @Transaction, na nag-aalis ng inter-connection Deadlock.

Paano natutukoy ng Android ang Deadlock?

Android Runtime ay may built-in na Deadlock detector na naa-activate sa pagbuo ng ANR (Application Not Responding). Sinusuri ng system ang Thread Dump ng lahat ng thread ng application at minamarkahan ang mutual blocking. Ang resulta ay magagamit sa /data/anr/traces.txt at Google Play Console sa seksyong ANR Reports.

Ano ang gagawin kung ang Deadlock ay natagpuan sa produksyon?

Una, kunin ang Thread Dump ng lahat ng thread ng application. Suriin kung anong mga lock ang hawak ng bawat thread at kung alin ang sinusubukan nitong makuha. Magpatupad ng Watchdog timer na may awtomatikong dump kapag lumampas sa limitasyon ng oras. Pagkatapos ng pag-aayos, magdagdag ng lint rule na ThreadSafety sa CI pipeline upang maiwasan ang pag-ulit.

Buod

  • Deadlock — mutual blocking kung saan ang mga thread ay walang katapusang naghihintay ng mga mapagkukunang inookupahan ng bawat isa
  • Apat na kondisyon ng Coffman (Mutual Exclusion, Hold and Wait, No Preemption, Circular Wait) ay kinakailangan para sa paglitaw ng Deadlock
  • Thread Dump — karaniwang paraan ng pagtuklas ng mutual blocking sa JVM at Android Runtime
  • Hierarkiya ng mga lock na may pinag-isang pandaigdigang pagkakasunud-sunod ay ganap na nag-aalis ng kondisyon ng paikot na paghihintay
  • TryLock na may timeout ay pumipigil sa walang katapusang paghihintay at nagpapahintulot sa thread na wastong pangasiwaan ang hindi pagkakaroon ng mapagkukunan
  • Deadlock vs Starvation — sa Deadlock ang mga thread ay naka-block, sa Starvation sila ay handa nang isagawa ngunit hindi nakakakuha ng CPU
  • Watchdog timer at static analyzer (ThreadSafe, Checker Framework) — pangunahing proteksyon laban sa Deadlock sa CI/CD

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