Višenitnost i Konkurentnost u mobilnom razvoju: šta je to, principi i kako radi

Аутор: IT Sectr Објављено: 2026-03-12 Време читања: 13 мин

Svaka mobilna aplikacija obavlja više zadataka istovremeno: učitava podatke iz mreže, obrađuje dodire korisnika, animira interfejs i čuva datoteke. Ako sav ovaj kod radi u jednoj niti, aplikacija se zamrzava pri svakom mrežnom kašnjenju. Višenitnost (multithreading) i konkurentnost su ključni koncepti koji omogućavaju aplikaciji da ostane responzivna i efikasna. U ovom članku ćemo pokriti sve glavne alate: od Main Thread i RunLoop do korutina u Kotlin i Combine na iOS-u. Materijal se zasniva na zvaničnoj Apple GCD dokumentaciji.

Ključne tačke

  • Main Thread — jedina nit za rad sa UI; svi ostali zadaci se premeštaju u pozadinu
  • GCD i OperationQueue — glavni mehanizmi višenitnosti u iOS-u
  • Coroutines i Flow — savremeni standard asinhronosti u Kotlin/Android
  • RxJava, RxSwift i Combine — reaktivni frejmvorci za rad sa tokovima podataka
  • Race Condition, Deadlock i Livelock — klasični problemi višenitnosti koji zahtevaju sinhronizaciju
  • Izbor alata zavisi od platforme i složenosti zadatka: za jednostavne pozive je dovoljan Async/Await, za složene tokove — Rx ili Combine

Šta je višenitnost?

Višenitnost je sposobnost aplikacije da istovremeno izvršava više delova koda. Svaki deo se izvršava u zasebnoj niti (Thread) — laganom procesu sa sopstvenim stekom poziva. U mobilnom razvoju, niti se dele u dve kategorije: Main Thread (UI nit) i Background Threads (pozadinske niti).

Operativni sistem sam upravlja raspodelom niti po jezgrima procesora. Moderni uređaji imaju 6–8 jezgara, tako da paralelno izvršavanje može ubrzati rad. Međutim, kreiranje niti je skupa operacija, pa se direktan rad sa Thread ne preporučuje. Umesto toga, koriste se apstrakcije višeg nivoa: DispatchQueue, OperationQueue, CoroutineDispatcher.

Konkurentnost (Concurrency) je širi pojam od višenitnosti. Konkurentnost znači da se zadaci mogu izvršavati "istovremeno" čak i na jednom jezgru putem promene konteksta. Asinhronost (Async/Await) je model programiranja u kojem zadatak ne blokira nit, već vraća kontrolu dok čeka rezultat. Moderni jezici (Kotlin, Swift, Dart) imaju ugrađenu podršku za Async/Await.

U IT Sectr-u, posebnu pažnju posvećujemo pravilnoj arhitekturi višenitnosti na početku projekta. Greške napravljene u ranoj fazi dovode do teško uhvatljivih bagova: trka podataka, mrtvih blokada i nestabilnosti aplikacije pod opterećenjem. Svaki naš projekat prolazi reviziju arhitekture konkurentnosti u fazi planiranja.

Glavne niti (Main/Background)

Main Thread (glavna nit) — jedina nit u mobilnoj aplikaciji koja ima pristup UI-ju. Na Android-u se zove UI Thread, na iOS-u — Main Thread. Sve operacije sa interfejsom — promena teksta, animacije, obrada dodira — izvršavaju se samo na Main Thread-u. Ako se na glavnoj niti izvršava teška operacija (učitavanje datoteke, parsiranje JSON-a), interfejs prestaje da reaguje. Na Android-u to dovodi do ANR (Application Not Responding), na iOS-u — do "zamrzavanja" ekrana.

Background Threads (pozadinske niti) su namenjene za sve što nije vezano za UI: mrežni zahtevi, rad sa bazom podataka, obrada slika, kriptografija. Nakon završetka, rezultat se prenosi na Main Thread za prikaz. Svaka platforma pruža svoje alate za prebacivanje između niti: DispatchQueue.main.async u iOS-u, runOnUiThread ili withContext(Dispatchers.Main) u Android-u.

RunLoop — petlja za obradu događaja na glavnoj niti iOS-a. RunLoop čeka događaje (dodiri, tajmeri, obaveštenja) i prosleđuje ih odgovarajućim rukovaocima. Na Android-u ekvivalent je Looper, povezan sa svakim Main Thread-om. Main Looper beskonačno izvlači poruke iz reda i prosleđuje ih Handler-u za obradu. Razumevanje RunLoop-a i Looper-a pomaže da se izbegnu curenje memorije i "mucanje" interfejsa.

GCD i OperationQueue (iOS)

Grand Central Dispatch (GCD) — Apple biblioteka za upravljanje višenitnošću na nivou C jezika. GCD radi sa DispatchQueue — redovima zadataka. Programer ne kreira niti ručno; GCD upravlja pool-om niti (Thread Pool), raspoređujući zadatke po dostupnim jezgrima procesora. DispatchQueue su dva tipa: Serial Queue (serijski red — zadaci se izvršavaju jedan za drugim) i Concurrent Queue (konkurentni red — zadaci se mogu izvršavati istovremeno).

Main DispatchQueue — serijski red vezan za glavnu nit. Global Queues — konkurentni redovi sa različitim prioritetima (QoS — Quality of Service): userInteractive, userInitiated, utility, background. Odabir odgovarajućeg QoS-a je kritičan za performanse: .userInteractive — za zadatke koji utiču na UI (animacije, renderovanje); .background — za vremenski nekritične zadatke (sinhronizacija, čišćenje keša).

OperationQueue — apstrakcija iznad GCD sa dodatnim mogućnostima: otkazivanje zadataka, postavljanje zavisnosti između operacija, kontrola maksimalnog broja konkurentnih operacija. Operacije su objekti klase Operation (ili BlockOperation). Primer: ako treba da učitate sliku, zatim primenite filter i tek onda je prikažete — OperationQueue sa zavisnostima to savršeno rešava. U GCD-u biste morali ručno da sinhronizujete ove korake pomoću DispatchGroup ili semafora.

Async/Await u Swift 5.5+ — moderna alternativa GCD-u. Ključne reči async i await čine asinhroni kod linearnim i čitljivim. Funkcije se označavaju kao async, a pozivi se čekaju putem await. Sistem sam upravlja promenom konteksta: podrazumevano, async funkcija se izvršava na pozadinskoj niti, dok se ažuriranje UI-ja izvršava na MainActor-u. @MainActor — atribut koji garantuje izvršavanje koda na glavnoj niti.

Coroutines i Flow (Kotlin)

Coroutines (korutine) — lagane niti za Kotlin razvijene od strane JetBrains-a. Za razliku od običnih niti, korutine nisu vezane za određeni Thread. Hiljade korutina mogu da se izvršavaju na više niti bez značajnog opterećenja. CoroutineScope upravlja životnim ciklusom korutina: viewModelScope je vezan za ViewModel, lifecycleScope — za Activity/Fragment. Kada se opseg uništi, sve korutine dete se automatski otkazuju.

Dispatchers određuju na kom pool-u niti se korutina izvršava: Dispatchers.Main — UI nit; Dispatchers.IO — za mrežne zahteve i diskovne operacije; Dispatchers.Default — za CPU-intenzivna izračunavanja. Za promenu dispečera koristi se withContext. Korutine podržavaju struktuiranu konkurentnost: svaka korutina ima roditelja, a kada se roditelj otkaže, sve korutine dete se otkazuju. Ovo sprečava curenje memorije i viseće zadatke.

Flow — hladni asinhroni tok podataka iz biblioteke korutina. Flow emituje vrednosti sekvencijalno: (1) proizvođač generiše podatke, (2) operatori transformišu tok, (3) kolektor konzumira rezultat. Za razliku od LiveData, Flow podržava složene lance operatora (map, filter, flatMapConcat, catch) i potpuno je bezbedan za niti. StateFlow i SharedFlow — vruće varijante Flow-a, idealne za stanje UI-ja i jednokratne događaje (Snackbar, navigacija).

Channel — još jedna apstrakcija korutina za prenos podataka između korutina. Channel radi kao red: jedan pošiljalac (send) i jedan ili više primalaca (receive). Baferovani kanali (Channel(UNLIMITED), Channel(BUFFERED)) omogućavaju konfigurisanje ponašanja pri prekoračenju. Channel se često koristi sa Flow-om za povezivanje API-ja zasnovanih na povratnim pozivima sa korutinama: callbackFlow { … }.

U IT Sectr-u aktivno koristimo korutine i Flow u svim Android projektima. Ovo omogućava pisanje asinhronog koda koji izgleda sinhrono, lako se testira (runTest, TestDispatcher) i ne zahteva ručno upravljanje nitima. Primer jednostavne korutine sa učitavanjem podataka:

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 i Combine

Reaktivno programiranje — paradigma u kojoj se podaci šire kao asinhroni tokovi (Observable, Publisher). RxJava/RxKotlin — najpopularnija implementacija za Android, portovana iz .NET Rx. RxSwift — slična biblioteka za iOS. Glavne komponente: Observable (izvor događaja), Observer (pretplatnik), Scheduler (upravljanje nitima), Operators (transformacija toka).

Combine — Apple frejmvork za reaktivno programiranje predstavljen u iOS 13. Combine koristi protokole Publisher (izdavač) i Subscriber (pretplatnik). Za razliku od RxSwift, Combine je ugrađen u SDK i blisko integrisan sa SwiftUI. Operatori u Combine: map, filter, combineLatest, zip, debounce, throttle — pokrivaju većinu scenarija: od povezivanja podataka sa UI do debaunsa upita za pretragu.

Future i Promise — obrasci za rad sa jednim asinhronim rezultatom. Future predstavlja vrednost koja će biti dostupna kasnije. Promise je obećanje da će se obezbediti vrednost. U Rx-u je to Single (jedan uspešan odgovor ili greška), u Combine — Future Publisher. U praksi, Future/Promise su zgodni za pojedinačne API zahteve, dok su Observable/Publisher za kontinuirane tokove (geolokacija, unos teksta).

Callback i Delegate — klasični obrasci za asinhrone operacije. Callback — funkcija prosleđena kao argument i pozvana po završetku operacije. Delegate — objekat koji implementira protokol sa metodama za obradu događaja. Nedostatak: "callback pakao" (ugneždeni callback-ovi) i složenost obrade grešaka. NotificationCenter (iOS) i EventBus (Android) — mehanizmi emitovanja događaja, korisni za labavo povezanu komunikaciju, ali vode ka implicitnim zavisnostima.

Problemi višenitnosti (Race Condition, Deadlock)

Višenitnost otvara vrata visokim performansama, ali istovremeno stvara rizik od teško uhvatljivih grešaka. Najčešće: Race Condition (stanje trke), Deadlock (mrtva blokada), Livelock (živa blokada) i Starvation (gladovanje niti). Razumevanje ovih problema je obavezna veština svakog mobilnog programera.

Race Condition

Race Condition nastaje kada dve ili više niti istovremeno čitaju i pišu iste podatke bez sinhronizacije. Rezultat zavisi od toga koja nit se prva izvrši. Klasičan primer: dve niti povećavaju brojač. Operacija "pročitaj → povećaj → napiši" nije atomska, pa se pri istovremenom izvršavanju jedan inkrement "gubi". Rešenje — korišćenje atomskih operacija (AtomicInteger, AtomicReference) ili blokada (Mutex, Semaphore, synchronized).

Deadlock

Deadlock — situacija u kojoj svaka nit drži resurs i čeka resurs koji drži druga nit. Nijedna nit ne može da nastavi. Uslovi nastanka: međusobno isključivanje, držanje i čekanje, bez preduzimanja, ciklično čekanje. Prevencija: uspostavljanje jedinstvenog redosleda zauzimanja blokada, korišćenje tryLock sa vremenskim ograničenjem, primena Lock-Free algoritama (ConcurrentHashMap, CopyOnWriteArrayList).

Livelock i Starvation

Livelock — niti nisu blokirane, ali neprestano "prosleđuju" resurse jedna drugoj bez obavljanja korisnog rada. Primer: dve osobe se sretnu u hodniku i obe ustupe put, krećući se u istom smeru. Starvation — nit ne dobija pristup resursu jer ga druge niti stalno preotimaju. Rešenje: pravične blokade (fair locks), prioriteti niti sa oprezom.

Alati za sinhronizaciju

Za sprečavanje problema višenitnosti koriste se primitivi sinhronizacije: Mutex (međusobno isključivanje), Semaphore (ograničenje broja istovremenih pristupa), Lock (interfejs sa tryLock), Synchronized (blokada na nivou JVM), @MainActor (Swift — garancija izvršavanja na glavnoj niti). Na Android-u je dostupan i ThreadPool (pool niti) preko Executors.newFixedThreadPool, newCachedThreadPool. Međutim, ručno upravljanje pool-ovima je prerogativa starih projekata; u novim projektima je bolje koristiti korutine.

Alat Platforma Tip Karakteristike
DispatchQueue (GCD)iOSRed zadatakaSerijski/Konkurentni, QoS prioriteti, Thread Pool vodi sistem
OperationQueueiOSRed operacijaZavisnosti, otkazivanje, maxConcurrentOperationCount
Coroutines + FlowAndroidKorutineLagane, struktuirana konkurentnost, StateFlow, Channel
RxJava / RxKotlinAndroidReaktivni tokObservable, Schedulers, bogat skup operatora
CombineiOSReaktivni tokPublisher/Subscriber, SwiftUI integracija
Async/Await + TaskiOS / AndroidAsinhroni modelLinearni kod, @MainActor, struktuirana konkurentnost

Često postavljana pitanja

Koja je razlika između Main Thread i Background Thread?

Main Thread (UI nit) je odgovoran za renderovanje interfejsa i obradu dodira. Background Thread obavlja pozadinske zadatke — učitavanje podataka, proračune, mrežni rad. Blokiranje Main Thread-a izaziva zamrzavanje interfejsa (ANR na Android-u, frozen UI na iOS-u).

Šta je Race Condition i kako je izbeći?

Race Condition — stanje trke kada dve niti istovremeno pristupaju deljenim podacima i rezultat zavisi od redosleda izvršavanja. Izbegava se sinhronizacijom: Mutex, Semaphore, Lock, Synchronized, @MainActor ili atomskim operacijama.

Coroutines ili RxJava: šta izabrati za Android?

Coroutines su savremeni standard za Android (JetBrains, podržan od Google). RxJava/RxKotlin je reaktivni pristup sa bogatim skupom operatora. Coroutines su jednostavnije za asinhrone pozive, RxJava je moćniji za složene tokove podataka. U IT Sectr-u koristimo Coroutines + Flow za nove projekte.

Šta su Deadlock i Livelock?

Deadlock — međusobno blokiranje gde dve niti čekaju resurse jedna druge. Livelock — niti nisu blokirane, ali stalno prosleđuju resurse bez korisnog rada. Oba problema se rešavaju pravilnim redosledom blokada i vremenskim ograničenjima.

Zašto je potreban DispatchQueue u iOS-u?

DispatchQueue je apstrakcija Grand Central Dispatch (GCD) za upravljanje nitima. Main Queue izvršava zadatke na glavnoj niti, Global Queues — na pozadinskim nitima. Serial Queue garantuje sekvencijalno izvršavanje, Concurrent Queue — paralelno. U modernim projektima, GCD se često zamenjuje sa Async/Await i Task.

Sažetak

  • Main Thread — samo UI; sve ostale operacije u pozadini
  • GCD i OperationQueue — osnova višenitnosti na iOS-u; Async/Await — moderna alternativa
  • Coroutines i Flow — standard za Android; struktuirana konkurentnost sprečava curenje
  • RxJava, RxSwift, Combine — reaktivni frejmvorci za složene tokove podataka
  • Race Condition i Deadlock — glavni problemi; rešavaju se blokadama i pravilnim redosledom zauzimanja resursa
  • Thread Pool vodi sistem (GCD) ili frejmvork (korutine); ručno kreiranje niti se ne preporučuje
  • Izbor alata zavisi od platforme: Coroutines za Android, GCD/Combine za iOS

Развићемо мобилну апликацију под кључ

IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту