Multithreading dan Konkurensi dalam Pengembangan Seluler: Apa Itu, Prinsip dan Cara Kerjanya

Penulis: IT Sectr Diterbitkan: 2026-03-12 Waktu membaca: 13 mnt

Setiap aplikasi seluler melakukan banyak tugas secara bersamaan: memuat data dari jaringan, memproses sentuhan pengguna, menganimasikan antarmuka, dan menyimpan file. Jika semua kode ini berjalan dalam satu thread, aplikasi akan membeku pada setiap keterlambatan jaringan. Multithreading dan konkurensi adalah konsep kunci yang memungkinkan aplikasi tetap responsif dan efisien. Dalam artikel ini kita akan membahas semua alat utama: dari Main Thread dan RunLoop hingga Coroutines Kotlin dan Combine di iOS. Materi ini didasarkan pada dokumentasi resmi Apple GCD.

Poin Penting

  • Main Thread — satu-satunya thread untuk bekerja dengan UI; semua tugas lainnya dipindahkan ke Background
  • GCD dan OperationQueue — mekanisme multithreading utama di iOS
  • Coroutines dan Flow — standar modern untuk asinkronisitas di Kotlin/Android
  • RxJava, RxSwift dan Combine — framework reaktif untuk bekerja dengan aliran data
  • Race Condition, Deadlock dan Livelock — masalah multithreading klasik yang memerlukan sinkronisasi
  • Pilihan alat tergantung pada platform dan kompleksitas tugas: untuk panggilan sederhana, Async/Await sudah cukup, untuk aliran kompleks — Rx atau Combine

Apa itu Multithreading?

Multithreading adalah kemampuan aplikasi untuk menjalankan beberapa fragmen kode secara bersamaan. Setiap fragmen berjalan dalam thread terpisah — proses ringan dengan tumpukan panggilannya sendiri. Dalam pengembangan seluler, thread dibagi menjadi dua kategori: Main Thread (thread UI) dan Background Threads (thread latar belakang).

Sistem operasi sendiri mengelola distribusi thread di seluruh inti prosesor. Perangkat modern memiliki 6–8 inti, sehingga eksekusi paralel dapat mempercepat kerja. Namun, membuat thread adalah operasi yang mahal, jadi bekerja langsung dengan Thread tidak disarankan. Sebagai gantinya, abstraksi tingkat lebih tinggi digunakan: DispatchQueue, OperationQueue, CoroutineDispatcher.

Konkurensi (Concurrency) adalah konsep yang lebih luas dari multithreading. Konkurensi berarti bahwa tugas dapat dijalankan "secara bersamaan" bahkan pada satu inti melalui peralihan konteks. Asinkronisitas (Async/Await) adalah model pemrograman di mana tugas tidak memblokir thread, tetapi mengembalikan kontrol sambil menunggu hasil. Bahasa modern (Kotlin, Swift, Dart) memiliki dukungan Async/Await bawaan.

Di IT Sectr, kami memberikan perhatian khusus pada arsitektur multithreading yang benar di awal proyek. Kesalahan yang dilakukan pada tahap awal menyebabkan bug yang sulit ditangkap: perlombaan data, deadlock, dan ketidakstabilan aplikasi di bawah beban. Setiap proyek kami menjalani tinjauan arsitektur konkurensi pada tahap perencanaan.

Thread Utama (Main/Background)

Main Thread (thread utama) — satu-satunya thread dalam aplikasi seluler yang memiliki akses ke UI. Di Android disebut UI Thread, di iOS — Main Thread. Semua operasi antarmuka — mengubah teks, animasi, memproses sentuhan — dilakukan hanya di Main Thread. Jika operasi berat (memuat file, parsing JSON) dilakukan di thread utama, antarmuka berhenti merespons. Di Android ini menyebabkan ANR (Application Not Responding), di iOS — layar "membeku".

Background Threads (thread latar belakang) ditujukan untuk semua yang tidak terkait dengan UI: permintaan jaringan, operasi basis data, pemrosesan gambar, kriptografi. Setelah selesai, hasilnya diteruskan ke Main Thread untuk ditampilkan. Setiap platform menyediakan alatnya sendiri untuk beralih antar thread: DispatchQueue.main.async di iOS, runOnUiThread atau withContext(Dispatchers.Main) di Android.

RunLoop — loop pemrosesan acara di thread utama iOS. RunLoop menunggu acara (sentuhan, timer, notifikasi) dan mendistribusikannya ke penangan yang sesuai. Di Android, padanannya adalah Looper, yang terkait dengan setiap Main Thread. Main Looper tanpa henti mengekstrak pesan dari antrian dan meneruskannya ke Handler untuk diproses. Memahami RunLoop dan Looper membantu menghindari kebocoran memori dan "gagap" antarmuka.

GCD dan OperationQueue (iOS)

Grand Central Dispatch (GCD) — pustaka Apple untuk mengelola multithreading di tingkat bahasa C. GCD bekerja dengan DispatchQueue — antrian tugas. Pengembang tidak membuat thread secara manual; GCD mengelola kumpulan thread (Thread Pool), mendistribusikan tugas ke seluruh inti prosesor yang tersedia. DispatchQueue terdiri dari dua jenis: Serial Queue (antrian serial — tugas dijalankan satu per satu) dan Concurrent Queue (antrian konkuren — tugas dapat dijalankan secara bersamaan).

Main DispatchQueue — antrian serial yang terikat ke thread utama. Global Queues — antrian konkuren dengan prioritas berbeda (QoS — Quality of Service): userInteractive, userInitiated, utility, background. Memilih QoS yang tepat sangat penting untuk kinerja: .userInteractive — untuk tugas yang memengaruhi UI (animasi, rendering); .background — untuk tugas yang tidak kritis terhadap waktu (sinkronisasi, pembersihan cache).

OperationQueue — abstraksi di atas GCD dengan kemampuan tambahan: pembatalan tugas, pengaturan dependensi antar operasi, kontrol jumlah maksimum operasi konkuren. Operasi adalah objek dari kelas Operation (atau BlockOperation). Contoh: jika Anda perlu memuat gambar, lalu menerapkan filter, dan baru kemudian menampilkannya — OperationQueue dengan dependensi menanganinya dengan sempurna. Di GCD Anda harus menyinkronkan langkah-langkah ini secara manual menggunakan DispatchGroup atau semafor.

Async/Await di Swift 5.5+ — alternatif modern untuk GCD. Kata kunci async dan await membuat kode asinkron menjadi linier dan mudah dibaca. Fungsi ditandai sebagai async, dan panggilan ditunggu melalui await. Sistem itu sendiri mengelola peralihan konteks: secara default, fungsi async dijalankan di thread latar belakang, sementara pembaruan UI dijalankan di MainActor. @MainActor — atribut yang menjamin eksekusi kode di thread utama.

Coroutines dan Flow (Kotlin)

Coroutines (korutin) — thread ringan untuk Kotlin yang dikembangkan oleh JetBrains. Tidak seperti thread biasa, korutin tidak terikat pada Thread tertentu. Ribuan korutin dapat berjalan di beberapa thread tanpa overhead yang signifikan. CoroutineScope mengelola siklus hidup korutin: viewModelScope terikat ke ViewModel, lifecycleScope — ke Activity/Fragment. Ketika ruang lingkup dihancurkan, semua korutin anak secara otomatis dibatalkan.

Dispatchers menentukan di kumpulan thread mana korutin dijalankan: Dispatchers.Main — thread UI; Dispatchers.IO — untuk permintaan jaringan dan operasi disk; Dispatchers.Default — untuk komputasi intensif CPU. Untuk mengganti dispatcher, digunakan withContext. Korutin mendukung konkurensi terstruktur: setiap korutin memiliki induk, dan ketika induk dibatalkan, semua korutin anak dibatalkan. Ini mencegah kebocoran memori dan tugas yang menggantung.

Flow — aliran data asinkron dingin dari pustaka korutin. Flow mengeluarkan nilai secara berurutan: (1) produsen menghasilkan data, (2) operator mengubah aliran, (3) kolektor mengonsumsi hasil. Tidak seperti LiveData, Flow mendukung rantai operator yang kompleks (map, filter, flatMapConcat, catch) dan sepenuhnya aman untuk thread. StateFlow dan SharedFlow — varian panas dari Flow, ideal untuk status UI dan acara satu kali (Snackbar, navigasi).

Channel — abstraksi korutin lain untuk meneruskan data antar korutin. Channel bekerja seperti antrian: satu pengirim (send) dan satu atau lebih penerima (receive). Saluran yang di-buffer (Channel(UNLIMITED), Channel(BUFFERED)) memungkinkan mengonfigurasi perilaku saat meluap. Channel sering digunakan bersama dengan Flow untuk menjembatani API berbasis callback ke korutin: callbackFlow { … }.

Di IT Sectr kami secara aktif menggunakan korutin dan Flow di semua proyek Android. Ini memungkinkan penulisan kode asinkron yang terlihat sinkron, mudah diuji (runTest, TestDispatcher) dan tidak memerlukan manajemen thread manual. Contoh korutin sederhana dengan pemuatan data:

kotlin
class UserRepository(
    private val api: UserApi,
    private val dao: UserDao
) {
    suspend fun getUsers(): List<User> = withContext(Dispatchers.IO) {
        return@withContext try {
            val users = api.fetchUsers()
            dao.insertAll(users)
            users
        } catch (e: Exception) {
            dao.getAll()
        }
    }
}

Rx dan Combine

Pemrograman reaktif — paradigma di mana data menyebar sebagai aliran asinkron (Observable, Publisher). RxJava/RxKotlin — implementasi paling populer untuk Android, diporting dari .NET Rx. RxSwift — pustaka serupa untuk iOS. Komponen utama: Observable (sumber acara), Observer (pelanggan), Scheduler (manajemen thread), Operators (transformasi aliran).

Combine — framework Apple untuk pemrograman reaktif yang diperkenalkan di iOS 13. Combine menggunakan protokol Publisher (penerbit) dan Subscriber (pelanggan). Tidak seperti RxSwift, Combine terintegrasi dalam SDK dan terintegrasi erat dengan SwiftUI. Operator di Combine: map, filter, combineLatest, zip, debounce, throttle — mencakup sebagian besar skenario: dari pengikatan data ke UI hingga debounce kueri pencarian.

Future dan Promise — pola untuk bekerja dengan satu hasil asinkron. Future mewakili nilai yang akan tersedia nanti. Promise adalah janji untuk memberikan nilai. Di Rx ini adalah Single (satu respons berhasil atau kesalahan), di Combine — Future Publisher. Dalam praktiknya, Future/Promise nyaman untuk permintaan API tunggal, sementara Observable/Publisher — untuk aliran berkelanjutan (geolokasi, masukan teks).

Callback dan Delegate — pola klasik untuk operasi asinkron. Callback — fungsi yang diteruskan sebagai argumen dan dipanggil saat operasi selesai. Delegate — objek yang mengimplementasikan protokol dengan metode penangan acara. Kekurangan: "callback hell" (callback bersarang) dan kompleksitas penanganan kesalahan. NotificationCenter (iOS) dan EventBus (Android) — mekanisme penyiaran acara, berguna untuk komunikasi yang terhubung secara longgar tetapi menyebabkan ketergantungan implisit.

Masalah Multithreading (Race Condition, Deadlock)

Multithreading membuka pintu menuju kinerja tinggi, tetapi pada saat yang sama menciptakan risiko kesalahan yang sulit ditangkap. Yang paling umum: Race Condition (kondisi balapan), Deadlock (kebuntuan), Livelock (kebuntuan aktif) dan Starvation (kelaparan thread). Memahami masalah ini adalah keterampilan wajib bagi setiap pengembang seluler.

Race Condition

Race Condition terjadi ketika dua atau lebih thread secara bersamaan membaca dan menulis data yang sama tanpa sinkronisasi. Hasilnya tergantung pada thread mana yang dijalankan pertama. Contoh klasik: dua thread meningkatkan penghitung. Operasi "baca → tingkatkan → tulis" tidak atomik, jadi ketika dijalankan bersamaan, satu peningkatan "hilang". Solusinya — menggunakan operasi atomik (AtomicInteger, AtomicReference) atau kunci (Mutex, Semaphore, synchronized).

Deadlock

Deadlock — situasi di mana setiap thread memegang sumber daya dan menunggu sumber daya yang dipegang oleh thread lain. Tidak ada thread yang dapat melanjutkan. Kondisi terjadinya: saling eksklusif, menahan dan menunggu, tanpa preemption, menunggu sirkuler. Pencegahan: menetapkan urutan akuisisi kunci tunggal, menggunakan tryLock dengan batas waktu, menerapkan algoritma Lock-Free (ConcurrentHashMap, CopyOnWriteArrayList).

Livelock dan Starvation

Livelock — thread tidak diblokir tetapi terus-menerus "melewatkan" sumber daya satu sama lain tanpa melakukan pekerjaan yang berguna. Contoh: dua orang bertemu di koridor dan keduanya menepi, bergerak ke arah yang sama. Starvation — sebuah thread tidak mendapatkan akses ke sumber daya karena thread lain terus-menerus mencegatnya. Solusi: kunci yang adil (fair locks), prioritas thread dengan hati-hati.

Alat Sinkronisasi

Untuk mencegah masalah multithreading, primitif sinkronisasi digunakan: Mutex (saling eksklusif), Semaphore (membatasi jumlah akses bersamaan), Lock (antarmuka dengan tryLock), Synchronized (kunci tingkat JVM), @MainActor (Swift — menjamin eksekusi di thread utama). Di Android juga tersedia ThreadPool (kumpulan thread) melalui Executors.newFixedThreadPool, newCachedThreadPool. Namun, manajemen kumpulan manual adalah hak prerogatif proyek lama; di proyek baru lebih baik menggunakan korutin.

Alat Platform Tipe Fitur
DispatchQueue (GCD)iOSAntrian tugasSerial/Konkuren, prioritas QoS, Thread Pool dikelola sistem
OperationQueueiOSAntrian operasiDependensi, pembatalan, maxConcurrentOperationCount
Coroutines + FlowAndroidKorutinRingan, konkurensi terstruktur, StateFlow, Channel
RxJava / RxKotlinAndroidAliran reaktifObservable, Schedulers, set operator yang kaya
CombineiOSAliran reaktifPublisher/Subscriber, integrasi SwiftUI
Async/Await + TaskiOS / AndroidModel asinkronKode linier, @MainActor, konkurensi terstruktur

Pertanyaan yang Sering Diajukan

Apa perbedaan antara Main Thread dan Background Thread?

Main Thread (thread UI) bertanggung jawab untuk merender antarmuka dan memproses sentuhan. Background Thread melakukan tugas latar belakang — memuat data, perhitungan, pekerjaan jaringan. Memblokir Main Thread menyebabkan antarmuka membeku (ANR di Android, frozen UI di iOS).

Apa itu Race Condition dan bagaimana cara menghindarinya?

Race Condition — kondisi balapan ketika dua thread secara bersamaan mengakses data bersama dan hasilnya tergantung pada urutan eksekusi. Dihindari melalui sinkronisasi: Mutex, Semaphore, Lock, Synchronized, @MainActor atau operasi atomik.

Coroutines atau RxJava: mana yang harus dipilih untuk Android?

Coroutines adalah standar modern untuk Android (JetBrains, didukung oleh Google). RxJava/RxKotlin adalah pendekatan reaktif dengan set operator yang kaya. Coroutines lebih sederhana untuk panggilan asinkron, RxJava lebih kuat untuk aliran data yang kompleks. Di IT Sectr kami menggunakan Coroutines + Flow untuk proyek baru.

Apa itu Deadlock dan Livelock?

Deadlock — kebuntuan saling ketika dua thread menunggu sumber daya satu sama lain. Livelock — thread tidak diblokir tetapi terus-menerus melewatkan sumber daya tanpa melakukan pekerjaan berguna. Kedua masalah diselesaikan dengan urutan kunci yang tepat dan batas waktu.

Mengapa DispatchQueue diperlukan di iOS?

DispatchQueue adalah abstraksi dari Grand Central Dispatch (GCD) untuk manajemen thread. Main Queue menjalankan tugas di thread utama, Global Queues — di thread latar belakang. Serial Queue menjamin eksekusi berurutan, Concurrent Queue — paralel. Dalam proyek modern, GCD sering digantikan oleh Async/Await dan Task.

Ringkasan

  • Main Thread — hanya UI; semua operasi lainnya di Background
  • GCD dan OperationQueue — dasar multithreading di iOS; Async/Await — alternatif modern
  • Coroutines dan Flow — standar untuk Android; konkurensi terstruktur mencegah kebocoran
  • RxJava, RxSwift, Combine — framework reaktif untuk aliran data kompleks
  • Race Condition dan Deadlock — masalah utama; diselesaikan dengan kunci dan urutan akuisisi sumber daya yang tepat
  • Thread Pool dikelola oleh sistem (GCD) atau framework (korutin); pembuatan thread manual tidak disarankan
  • Pilihan alat tergantung pada platform: Coroutines untuk Android, GCD/Combine untuk iOS

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