Race Condition sa mga mobile app: esensya, mga sanhi ng pagkakaroon, at mga paraan ng pag-iwas

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

Ang Race Condition ay isang sitwasyon sa multi-thread programming kung saan ang huling resulta ay nakadepende sa pagkakasunod-sunod ng pagpapatakbo ng mga thread. Ayon sa dokumentasyon ng Oracle Java Tutorials (2024), ang kalagayan ng karera ay lumilitaw kapag sabay-sabay na pag-access sa isang shared resource nang walang synchronisasyon. Kung walang tamang mekanismo, ang Race Condition ay humahantong sa pagkasira ng data at hindi ma-reproduce na mga bug sa mga mobile application.

Mga pangunahing punto

  • Race Condition — isang depekto sa multi-thread code kung saan ang resulta ng pagpapatakbo ay nakadepende sa pagkakasunod-sunod ng mga thread
  • Kalagayan ng karera ay lumilitaw kapag walang synchronisasyon sa pag-access sa shared resource
  • Karera ng data — isang subtype ng Race Condition na may kaugnayan sa sabay-sabay na pagsulat at pagbasa ng variable
  • Mutex at semaphore — ang mga pangunahing kasangkapan para sa pag-aalis ng kalagayan ng karera sa mobile development
  • Atomikong operasyon ay nag-gagarantiya ng hindi paghahati ng pagpapatakbo at pumipigil sa karera ng thread

Ano ang Race Condition?

Race Condition (kalagayan ng karera) ay isang error sa multi-thread program kung saan ang kawastuhan ng operasyon ay nakadepende sa hindi mahuhulaan na pagkakasunod-sunod ng pagpapatakbo ng mga thread. Kapag dalawa o higit pang thread ang sabay-sabay na nag-access sa isang shared resource nang walang synchronisasyon, ang huling kalagayan ng resource ay nagiging hindi tiyak.

Sa mobile development, ang Race Condition ay partikular na mapanganib dahil ang mga thread ay maaaring isagawa sa iba't ibang core ng processor na may iba't ibang bilis. Hindi makokontrol ng developer kung aling thread ang unang makakatapos ng operasyon — ito ay pinagpapasyahan ng scheduler ng operating system. Ayon sa pananaliksik ng IBM (Concurrency Bugs in Android, 2022), humigit-kumulang 23% ng mga kritikal na bug sa Android application ay may kaugnayan sa kalagayan ng karera.

Ang pangunahing katangian ng Race Condition ay ang non-determinism nito. Ang parehong code ay maaaring gumana nang walang error nang libu-libong beses, pagkatapos ay biglang mag-crash. Ginagawa nitong partikular na mahirap ang diagnosis: ang bug ay nagpapakita lamang sa ilalim ng tiyak na kalagayan — CPU load, bilang ng aktibong thread, at phase ng pag-iskedyul.

Paano lumilitaw ang kalagayan ng karera

Hindi atomikong operasyon

Ang Race Condition ay lumilitaw kapag ang isang thread ay nagsasagawa ng hindi atomikong operasyon — isang pagkakasunod-sunod ng ilang hakbang na maaaring maabala ng isa pang thread. Halimbawa, ang operasyon ng increment na counter++ ay talagang binubuo ng tatlong hakbang: pagbasa ng halaga mula sa memorya, pagtaas ng isa, at pagsulat pabalik. Kung dalawang thread ang magsasagawa ng mga hakbang na ito nang magkahalo, ang resulta ay magiging mali.

Kawalan ng synchronisasyon

Ang pangunahing sanhi ng kalagayan ng karera — kawalan ng synchronisasyon sa pag-access sa shared data. Kapag ang isang thread ay nagbabago ng object at ang isa naman ay sabay itong binabasa, ang resulta ng pagbasa ay hindi mahuhulaan. Sa Android, ang problemang ito ay pinalala ng katotohanan na ang mga component ng application (Activity, Service, BroadcastReceiver) ay maaaring isagawa sa iba't ibang thread.

Maling paggamit ng coroutine

Sa modernong Android development sa Kotlin, ang Race Condition ay madalas na lumilitaw dahil sa maling paggamit ng coroutine. Kung dalawang coroutine ang gumagana sa shared state sa iba't ibang Dispatchers nang walang synchronisasyon, ang resulta ay hindi mahuhulaan. Ito ay lalo na madalas na nangyayari kapag pinagsasama ang Dispatchers.IO at Dispatchers.Main sa mga shared mutable object.

Halimbawa ng Race Condition sa Kotlin code

Tingnan natin ang isang klasikong halimbawa ng karera ng data — pag-increment ng counter mula sa maraming thread. Nang walang synchronisasyon, ang huling halaga ay magiging mas mababa kaysa sa inaasahan dahil ang mga operasyon ay nag-o-overlap.

kotlin
class RaceCounter {
    private var counter = 0

    fun increment() {
        // Hindi atomikong operasyon — tatlong hakbang
        counter++  // bumabasa, nagdaragdag, nagsusulat
    }

    fun getCount(): Int = counter
}

fun main() = runBlocking {
    val rc = RaceCounter()
    val jobs = List(1000) {
        launch(Dispatchers.Default) {
            rc.increment()
        }
    }
    jobs.forEach { it.join() }
    println(rc.getCount())  // Inaasahan 1000, nakukuha ~997
}

Sa halimbawang ito, 1000 coroutine ang sabay-sabay na tumatawag sa increment(). Dahil sa hindi pagka-atomiko ng operasyon na counter++, ang huling halaga ay halos hindi kailanman katumbas ng 1000. Bawat pagpapatakbo ay nagbibigay ng iba't ibang resulta — ang klasikong sintomas ng Race Condition. Kung mas maraming thread ang lumalahok sa karera, mas malaki ang paglihis mula sa inaasahang halaga.

Ang pag-aayos — paggamit ng atomikong uri o lock. Sa Kotlin, para sa gawaing ito ay angkop ang AtomicInteger mula sa package na java.util.concurrent.atomic. Ginagarantiya nito na ang mga operasyon ng basa-bago-sulat ay isinasagawa bilang isang hindi mahahati na aksyon sa antas ng processor.

kotlin
import java.util.concurrent.atomic.AtomicInteger

class SafeCounter {
    private val counter = AtomicInteger(0)

    fun increment() {
        counter.incrementAndGet()  // atomikong operasyon
    }

    fun getCount(): Int = counter.get()
}

Mga uri ng kalagayan ng karera

Karera ng data (Data Race)

Karera ng data — ang pinakakaraniwang uri ng Race Condition. Lumilitaw kapag ang isang thread ay nagsusulat ng data sa isang variable at ang isa naman ay sabay na bumabasa o nagsusulat ng parehong variable nang walang synchronisasyon. Sa Java Memory Model, ang ganitong pag-uugali ay itinuturing na hindi tiyak — ang thread ay maaaring makakita ng hindi napapanahong halaga dahil sa caching sa antas ng CPU.

Check-Then-Act

Ang pattern na Check-Then-Act — sitwasyon kung saan ang isang thread ay sumusuri ng kondisyon, pagkatapos ay nagsasagawa ng aksyon batay sa pagsusuring iyon. Sa pagitan ng pagsusuri at aksyon, ang isa pang thread ay maaaring magbago ng state. Tipikal na halimbawa: pagsusuri ng pagkakaroon ng elemento sa koleksyon at pagkatapos ay pagtanggal nito. Sa Android, ito ay madalas na nangyayari kapag nagtatrabaho sa SharedPreferences o database.

Read-Modify-Write

Read-Modify-Write — sitwasyon kung saan ang isang thread ay bumabasa ng halaga, binabago ito sa lokal na memorya, at isinusulat pabalik. Kung sa pagitan ng pagbasa at pagsulat ay binago ng isa pang thread ang orihinal na halaga, ang resulta ng pagbabago ay mawawala. Klasikong halimbawa — ang operasyon na counter++, na ipinaliwanag sa itaas sa Kotlin code.

Transactional memory (STM)

Software Transactional Memory (STM) — isang approach kung saan ang mga operasyon sa shared data ay isinasagawa sa mga transaksyon, katulad ng mga database. Kung dalawang transaksyon ang magkasalungat, ang isa ay ibinabalik at inuulit. Sa Kotlin para sa JVM, available ang library na Multiverse STM, na awtomatikong humahawak ng mga conflict sa pag-access nang walang tahasang lock. Ang STM ay lalong kapaki-pakinabang sa Android kapag nagtatrabaho sa maraming magkakaugnay na object.

Maninipis na karera sa Android UI

Isang espesyal na kategorya ng Race Condition — maninipis na karera (thin races), na may kaugnayan sa lifecycle ng Activity. Tipikal na scenario: isang background thread ay natatapos mag-load ng data, ngunit ang Activity ay nawasak na (pag-ikot ng screen). Sinusubukan ng coroutine na i-update ang hindi umiiral na View at nag-crash na may IllegalStateException. Solusyon — paggamit ng viewModelScope at Lifecycle-aware component na awtomatikong nagkakansela ng mga coroutine kapag nawasak ang Lifecycle Owner.

Paano matukoy ang Race Condition

Ang pagtukoy ng Race Condition ay isa sa pinakamahirap na gawain sa pag-debug ng multi-thread application. Ang standard na pagsubok ay bihirang nagpapakita ng kalagayan ng karera dahil ito ay lumilitaw lamang sa tiyak na timing. Ayon sa Google (Android Testing Guide, 2023), humigit-kumulang 70% ng Race Condition ay hindi natutukoy ng unit test dahil sa deterministikong pagkakasunod-sunod ng pagpapatakbo sa kapaligiran ng pagsubok.

Ang mga pangunahing paraan ng pagtukoy ay kinabibilangan ng mga espesyalisadong kasangkapan. ThreadSanitizer (TSan) — isang dynamic na analyzer na nakapaloob sa Android NDK, na sumusubaybay sa lahat ng pag-access sa memorya at nakakatukoy ng hindi naka-synchronize na pag-access. Para sa Java/Kotlin code, inirerekomenda ng Google ang Android Studio Layout Inspector kasama ng StrictMode, na pumipigil sa ilegal na pag-access sa UI thread mula sa background thread.

Isa pang epektibong approach — Stress Testing na may paulit-ulit na pagpapatakbo ng mga pagsubok sa ilalim ng load. Ang Lincheck framework mula sa JetBrains ay espesyal na idinisenyo para sa pagsubok ng mga konkurenteng data structure sa JVM. Awtomatiko itong bumubuo ng mga scenario na may iba't ibang permutasyon ng mga operasyon at sinusuri ang kawastuhan ng mga resulta sa bawat kaso.

KasangkapanPlatformUri ng pagsusuri
ThreadSanitizerAndroid NDKDynamic na pagsusuri ng memorya
Intel InspectorWindowsStatic + dynamic
LincheckJVM / KotlinStress testing
StrictModeAndroidRuntime interception

Mga paraan ng pag-iwas sa Race Condition

Atomikong variable

Atomikong variable (AtomicInteger, AtomicLong, AtomicReference) — ang pinakamadaling paraan upang alisin ang karera ng data para sa mga solong operasyon. Gumagamit sila ng mababang antas na CPU CAS instruction (Compare-And-Swap) na isinasagawa nang atomiko nang walang lock. Nagbibigay ito ng maximum na pagganap sa mga scenario na may mababang kumpetisyon.

Lock at Mutex

Mutex at lock — ang klasikong mekanismo ng synchronisasyon, angkop para sa mga komplikadong operasyon at kritikal na seksyon. Sa Kotlin para sa coroutine, ginagamit ang suspending Mutex mula sa library na kotlinx.coroutines, na sumusuporta sa suspensyon sa halip na pag-block ng thread. Ito ay umiiwas sa walang laman na paghihintay na katangian ng tradisyonal na lock.

Pag-iisa ng state

Pag-iisa ng state — isang arkitektural na approach kung saan ang bawat thread ay gumagana sa sarili nitong kopya ng data. Sa mobile development, ito ay nakakamit sa pamamagitan ng modelong Actor, kung saan ang bawat aktor ay nagmamay-ari ng sarili nitong state at nakikipagpalitan ng mensahe sa ibang aktor. Ang Kotlin Coroutines ay nagbibigay ng implementasyon ng Actor sa pamamagitan ng Channel at SendChannel, na ganap na nag-aalis ng Race Condition sa antas ng arkitektura.

Isang karagdagang antas ng proteksyon — Immutability: kung ang shared data ay sa prinsipyo hindi nababago, ang Race Condition ay nagiging imposible kahit walang synchronisasyon. Sa Kotlin, para dito ginagamit ang data class na may val field at koleksyon mula sa kotlinx.collections.immutable, na nag-gagarantiya ng hindi pagbabago ng istraktura kapag nai-publish sa pagitan ng mga thread.

Mga madalas itanong

Ano ang pagkakaiba ng Race Condition at Data Race?

Data Race ay isang tiyak na uri ng Race Condition kung saan dalawang thread ang sabay-sabay na nag-a-access sa parehong memorya at kahit isa sa kanila ay nagsasagawa ng pagsulat. Ang Race Condition ay mas malawak na konsepto, na sumasaklaw sa lahat ng error na nakadepende sa pagkakasunod-sunod ng pagpapatakbo ng mga thread, kabilang ang mga lohikal na kalagayan ng karera.

Maaari bang ganap na alisin ang Race Condition sa Android?

Ang ganap na pag-alis ay hindi posible, ngunit maaaring mabawasan sa pinakamaliit. Gumamit ng mga hindi nababagong object (immutable), atomikong uri, at coroutine na may single-thread dispatcher. Ang mga kasangkapan ng static analysis, tulad ng Android Lint na may panuntunang ThreadSafety, ay tumutulong na matukoy ang mga potensyal na karera sa yugto ng compilation.

Paano nagpapakita ang Race Condition sa mga UI application?

Sa mga UI application ang Race Condition ay madalas na nagpapakita bilang pagkutitap ng screen, maling pagpapakita ng data, o pag-crash kapag nag-a-update ng listahan. Tipikal na scenario: isang background thread ay naglo-load ng data at nag-a-update ng adapter, habang ang user ay nag-scroll ng listahan sa parehong oras — nagkakaroon ng sabay-sabay na pag-access sa Adapter DataSet.

Ano ang volatile at nakakatulong ba ito laban sa Race Condition?

volatile ay nag-gagarantiya ng visibility ng mga pagbabago sa pagitan ng mga thread — ang pagsulat sa volatile variable ay agad na nakikita ng lahat ng thread. Gayunpaman, hindi nilulutas ng volatile ang problema ng Read-Modify-Write at Check-Then-Act, dahil hindi nito ginagarantiya ang atomicity ng mga komposisyong operasyon. Para sa mga ganitong scenario, kailangan ang mga lock o atomikong klase.

Paano naiiba ang Race Condition sa Kotlin Coroutines sa klasikong thread?

Sa Kotlin Coroutines, ang Race Condition ay lumilitaw sa antas ng scheduler ng coroutine, hindi scheduler ng thread ng operating system. Ang mga coroutine ay maaaring lumipat sa mga suspend point, na lumilikha ng karagdagang oportunidad para sa karera. Ang kasangkapan na kotlinx.coroutines.debug at debugger ng IntelliJ IDEA ay tumutulong sa pagsubaybay sa kalagayan ng coroutine.

Buod

  • Race Condition — error ng multi-thread code kung saan ang resulta ay nakadepende sa hindi mahuhulaan na pagkakasunod-sunod ng pagpapatakbo ng thread
  • Data Race — subtype ng kalagayan ng karera na lumilitaw sa sabay-sabay na hindi naka-synchronize na pag-access sa memorya na may pagsulat
  • Hindi atomikong operasyon (Read-Modify-Write, Check-Then-Act) — pangunahing sanhi ng pagkakaroon ng karera ng thread
  • ThreadSanitizer at Lincheck — mabisang kasangkapan para sa pagtukoy ng Race Condition sa yugto ng pagsubok
  • Atomikong variable (AtomicInteger) — pinakamainam na paraan ng proteksyon ng solong operasyon nang walang lock
  • Mutex at modelong Actor — arkitektural na approach para sa proteksyon ng mga komplikadong kritikal na seksyon
  • Pag-iisa ng state sa pamamagitan ng immutable object at single-thread dispatcher ay ganap na nag-aalis ng Race Condition sa antas ng disenyo

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