Starvation dalam aplikasi mobile — esensi, penyebab terjadinya, dan cara mencegah kelaparan thread

Penulis: IT Sectr Diterbitkan: 2026-03-18 Waktu membaca: 10 mnt

Starvation (kelaparan thread) — adalah situasi di mana sebuah thread tidak mendapatkan akses ke sumber daya yang diperlukan untuk melanjutkan pekerjaan, meskipun ia sendiri siap untuk dieksekusi. Menurut Baeldung (Java Thread Starvation, 2024), kelaparan terjadi karena penjadwalan yang tidak adil, ketika thread berprioritas rendah terus-menerus ditunda demi thread yang lebih berprioritas. Tidak seperti Deadlock, Starvation tidak memblokir thread — ia tetap dalam status RUNNABLE, tetapi tidak pernah mendapatkan waktu prosesor.

Poin utama

  • Starvation — situasi di mana thread tidak mendapatkan akses ke sumber daya meskipun siap untuk dieksekusi
  • Tidak seperti Deadlock, thread saat kelaparan tetap dalam status RUNNABLE — tidak diblokir, tetapi tidak berkembang
  • Penjadwalan yang tidak adil (misalnya, sinkronisasi melalui synchronized) — penyebab utama Starvation di JVM
  • Fair Lock (ReentrantLock(true)) menjamin urutan akses yang adil ke kunci dalam urutan antrian
  • Thread Priority dalam pengembangan mobile disarankan tidak diubah — Android Runtime sendiri mengelola prioritas

Apa itu Starvation?

Starvation (kelaparan thread) — adalah masalah pemrograman multi-thread di mana sebuah thread tidak dapat memperoleh akses ke sumber daya yang diperlukan untuk menyelesaikan tugas, meskipun sumber daya tidak diblokir selamanya oleh thread lain. Thread berada dalam status RUNNABLE, tetapi penjadwal atau mekanisme sinkronisasi secara sistematis menunda eksekusinya demi thread lain.

Dalam pengembangan mobile, Starvation muncul sebagai eksekusi tugas yang tidak merata: beberapa operasi dieksekusi seketika, yang lain — dengan penundaan yang parah. Misalnya, thread latar belakang yang menyinkronkan data mungkin tidak pernah mendapatkan akses ke database jika thread UI dan penangan animasi terus mendahuluinya. Menurut Android Developer Blog (Performance Matters, 2023), sekitar 12% kasus frame yang terlewat (jank) di Android disebabkan oleh Starvation tugas latar belakang yang menjadi sandaran rendering.

Perbedaan utama Starvation dari Deadlock — reversibilitas. Jika beban sistem menurun atau prioritas didistribusikan kembali, thread yang kelaparan dapat memperoleh sumber daya dan menyelesaikan pekerjaan. Namun, dalam kondisi beban tinggi yang konstan, Starvation dapat berlangsung tanpa batas, menciptakan kesan aplikasi yang macet.

Penyebab kelaparan thread

Kunci yang tidak adil (Non-Fair Locks)

synchronized di Java dan Kotlin — contoh klasik dari mekanisme yang tidak adil. Pada persaingan tinggi, JVM dapat memberikan kunci tanpa batas kepada thread aktif yang sama, sementara thread lain terus-menerus kalah dalam perlombaan. Ini bukan bug JVM, melainkan fitur implementasi: kunci yang tidak adil memberikan throughput yang lebih tinggi dengan mengorbankan keseragaman akses. Untuk aplikasi mobile dengan 4-8 thread, masalah ini sangat relevan.

Penggunaan prioritas yang salah

Menetapkan prioritas thread yang berbeda dapat menyebabkan Starvation thread berprioritas rendah. Di Android Runtime, penjadwal CFS (Completely Fair Scheduler) Linux mendistribusikan waktu prosesor secara proporsional dengan prioritas, dan jika thread berprioritas tinggi terus aktif, thread berprioritas rendah mungkin tidak pernah mendapatkan CPU. Google sangat tidak merekomendasikan mengubah prioritas thread di Android — sistem mengelolanya sendiri.

Bagian kritis yang panjang

Jika sebuah thread memegang kunci terlalu lama (menjalankan komputasi berat, permintaan jaringan, atau operasi file di dalam blok synchronized), thread lain yang menunggu kunci ini akan kelaparan. Ini sangat berbahaya di Android, di mana operasi panjang di thread UI menyebabkan ANR, dan memindahkannya ke thread latar belakang tanpa mengoptimalkan bagian kritis akan memindahkan masalah Starvation ke thread pekerja.

Contoh Starvation dalam kode Kotlin

Mari kita lihat contoh di mana satu thread mengambil kunci terlalu sering karena penjadwalan yang tidak adil. Starvation didemonstrasikan melalui loop tak terbatas dari thread berprioritas tinggi yang tidak memungkinkan thread berprioritas rendah mengakses sumber daya bersama.

kotlin
class SharedResource {
    private val lock = Any()

    fun criticalSection(id: String) {
        synchronized(lock) {
            println("$id mendapatkan akses")
            Thread.sleep(10)  // simulasi kerja
        }
    }
}

fun main() {
    val resource = SharedResource()

    // Thread prioritas tinggi — terus aktif
    val highPriority = Thread {
        while (true) {
            resource.criticalSection("High")
        }
    }

    // Thread prioritas rendah — mungkin tidak pernah mendapatkan akses
    val lowPriority = Thread {
        while (true) {
            resource.criticalSection("Low")
        }
    }

    highPriority.start()
    lowPriority.start()
    // "Low" mungkin tidak pernah menampilkan pesan — Starvation!
}

Dalam contoh ini, thread highPriority terus-menerus mengambil kunci dan melepaskannya hanya selama 10 ms. Karena sifat synchronized yang tidak adil, penjadwal JVM dengan probabilitas tinggi akan memberikan kunci lagi ke thread yang sama yang baru saja melepaskannya — thread berprioritas rendah kelaparan. Solusinya — menggunakan ReentrantLock(true) dengan flag fair, yang menjamin urutan dalam antrian tunggu.

Versi yang diperbaiki dengan fair lock memastikan distribusi akses yang adil ke sumber daya.

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

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

Starvation vs Deadlock vs Livelock

Tiga masalah klasik multi-threading — Starvation, Deadlock, dan Livelock — sering digabungkan, tetapi mekanisme dan cara penghapusannya berbeda. Starvation — thread siap tetapi tidak mendapatkan sumber daya. Deadlock — thread diblokir oleh penantian siklik. Livelock — thread aktif tetapi tidak berkembang.

ParameterStarvationDeadlockLivelock
Status threadRUNNABLEBLOCKEDRUNNABLE
KemajuanTidakTidakTidak (meskipun aktif)
Konsumsi CPURendahMinimalTinggi (hingga 100%)
PenyebabPenjadwalan tidak adilPenantian siklikReaksi yang sama terhadap konflik
Solusi utamaFair Lock, mengurangi bagian kritisHierarki kunciBatas percobaan ulang, backoff eksponensial

Starvation dianggap kurang kritis daripada Deadlock, karena tidak fatal — saat beban menurun, thread yang kelaparan pada akhirnya akan dieksekusi. Namun, dalam kondisi penggunaan nyata aplikasi Android, di mana memori dan CPU terbatas, Starvation dapat berlangsung selama beberapa menit, menciptakan UX yang tidak dapat diterima.

Cara mendeteksi Starvation

Thread Dump dengan pengambilan berulang pada interval pendek — metode dasar deteksi kelaparan. Jika sebuah thread secara konsisten berada dalam status RUNNABLE, tetapi tumpukan panggilannya tidak berubah selama beberapa dump — ini adalah tanda klasik Starvation. Di Android Studio, untuk ini digunakan Android Profiler dengan perekaman status thread dari waktu ke waktu.

Deteksi otomatis dimungkinkan melalui pemantauan waktu eksekusi tugas. Jika tugas dengan waktu eksekusi yang dapat diprediksi (misalnya, 50 ms) dieksekusi selama 5 detik atau lebih — ada kemungkinan besar Starvation. Dalam aplikasi mobile, Firebase Performance Monitoring memungkinkan konfigurasi jejak kustom (custom traces) untuk bagian kritis dan menerima notifikasi saat melebihi nilai ambang batas.

Untuk mendiagnosis Starvation akibat blok synchronized, gunakan Java Flight Recorder (JFR) (tersedia di Android melalui OpenJDK API) atau Async Profiler. Alat-alat ini menunjukkan monitor mana yang memiliki waktu tunggu terlama dan thread mana yang bersaing untuk setiap monitor. Data JFR terintegrasi dengan IntelliJ IDEA Ultimate melalui profiler bawaan.

Metode pencegahan kelaparan thread

Fair Lock (ReentrantLock dengan flag true)

ReentrantLock(true) menjamin bahwa thread menerima kunci dalam urutan antrian (FIFO). Tidak seperti synchronized, fair lock tidak memungkinkan situasi di mana thread yang baru saja melepaskan kunci segera mengambilnya kembali. Ini sepenuhnya menghilangkan Starvation, meskipun mengurangi throughput keseluruhan sebesar 10-20% karena overhead untuk memelihara antrian.

Struktur atomik tanpa kunci

Struktur data Lock-free (ConcurrentHashMap, AtomicReference, LongAdder) menghilangkan Starvation secara definisi, karena tidak mengandung kunci yang dapat dipegang oleh satu thread. Semua operasi menggunakan instruksi CAS prosesor, yang menjamin kemajuan setidaknya satu thread dalam sejumlah langkah terbatas. Untuk pengembangan mobile, pilih ConcurrentLinkedQueue untuk antrian tugas.

Bagian kritis yang pendek

Meminimalkan waktu memegang kunci — cara universal untuk mengurangi risiko Starvation. Keluarkan operasi berat (jaringan, I/O disk, komputasi kompleks) dari luar blok synchronized. Gunakan ReadWriteLock untuk skenario di mana pembaca tidak boleh kelaparan karena penulis yang jarang. Pustaka Kotlin Coroutines menyediakan Mutex dengan mekanisme suspending yang tidak memblokir thread OS.

Variabel kondisi dan sinyal

Condition.await() dan signal() harus digunakan dengan hati-hati: thread yang menunggu di Condition terbangun bersama dengan thread lain (spurious wakeup) dan semuanya bersaing untuk kunci. Jika satu thread setelah await segera kembali menunggu, sementara yang lain berhasil mengambil kunci — thread yang kelaparan dapat terbangun dan tertidur tanpa batas. Selalu periksa kondisi dalam loop while, bukan di if, untuk menjamin pemeriksaan ulang.

Pertanyaan yang sering diajukan

Apa perbedaan antara Starvation dan Priority Inversion?

Priority Inversion — adalah situasi di mana thread berprioritas rendah memegang kunci yang diperlukan oleh thread berprioritas tinggi. Akibatnya, thread berprioritas tinggi menunggu thread berprioritas rendah — prioritas menjadi terbalik. Starvation adalah masalah yang lebih luas: thread tidak mendapatkan sumber daya terlepas dari prioritas, karena penjadwalan yang tidak adil atau bagian kritis yang panjang.

Bisakah Starvation terjadi dalam aplikasi single-thread?

Tidak, Starvation — adalah masalah multi-threading. Dalam kode single-thread tidak ada persaingan untuk sumber daya dan penjadwalan thread. Namun, Starvation dapat terjadi dalam kode asinkron single-thread (misalnya, event loop JavaScript), jika satu mikro-tugas tanpa batas menunda eksekusi yang lain melalui setTimeout dengan penundaan nol.

Bagaimana Java Memory Model terkait dengan Starvation?

JMM (Java Memory Model) mendefinisikan aturan visibilitas perubahan antar thread, tetapi tidak menjamin penjadwalan yang adil. synchronized sesuai dengan JMM menyediakan konsistensi sekuensial — kebenaran dasar — tetapi tidak mencegah Starvation. Untuk keadilan, diperlukan mekanisme tambahan yang tidak termasuk dalam spesifikasi JMM.

Apa itu Starvation di thread UI Android?

Thread UI (Main Thread) tidak bisa kelaparan dalam arti klasik, karena memiliki prioritas tertinggi. Namun, Starvation terjadi ketika thread UI menunggu hasil dari thread latar belakang yang kelaparan. Skenario tipikal: AsyncTask atau coroutine memuat data, tetapi tidak dapat mengakses database karena persaingan dengan thread lain, dan UI membeku menunggu.

Bagaimana mencegah Starvation di Kotlin Coroutines?

Di coroutine, untuk mencegah Starvation gunakan limitedParallelism pada Dispatchers.IO untuk menghindari kehabisan thread. Untuk sinkronisasi, terapkan Mutex dari kotlinx.coroutines.sync — ia menangguhkan coroutine, bukan memblokir thread, yang mengurangi risiko kelaparan. Hindari runBlocking di coroutine, karena dapat mengambil alih thread pool dan menyebabkan Starvation coroutine lain.

Kesimpulan

  • Starvation — situasi di mana thread siap dieksekusi tetapi tidak mendapatkan sumber daya karena penjadwalan yang tidak adil
  • Tidak seperti Deadlock, saat kelaparan thread dalam status RUNNABLE dan dapat dieksekusi saat beban menurun
  • Kunci yang tidak adil (synchronized) dan penggunaan prioritas yang salah — penyebab utama Starvation
  • Fair Lock (ReentrantLock dengan flag true) menjamin urutan akses FIFO dan sepenuhnya menghilangkan kelaparan
  • Struktur Lock-free (ConcurrentHashMap, AtomicReference) menghilangkan Starvation di tingkat arsitektur
  • Thread Dump dengan pengambilan berulang dan Java Flight Recorder — metode diagnostik Starvation yang efektif
  • Bagian kritis yang pendek dan ReadWriteLock mengurangi kemungkinan kelaparan di sistem dengan beban tinggi

Kami akan mengembangkan aplikasi seluler turnkey

IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.

Diskusikan proyek

Baca juga