Thread — základní jednotka procesorového času, která má vlastní zásobník a běží nezávisle na ostatních vláknech. V mobilním vývoji se vlákna používají pro paralelní provádění úloh, aby rozhraní zůstalo responzivní během dlouhých operací. Android podporuje java.lang.Thread, Executors a Kotlin Coroutines, iOS — Thread (Objective-C), GCD a OperationQueue. Podle Android Thread Documentation vyžaduje vytvoření nativního vlákna alokaci ~1 MB pro zásobník operačním systémem.
Hlavní body
Thread (vlákno provádění) — nezávislá posloupnost instrukcí, kterou může operační systém naplánovat na jádro CPU. Každý proces (aplikace) obsahuje alespoň jedno vlákno — Main Thread. Další vlákna se vytvářejí pro paralelní provádění úloh. Každé vlákno má vlastní programový zásobník (s lokálními proměnnými), čítač instrukcí (PC) a registry. Paměť haldy (heap) je společná pro všechna vlákna procesu.
V mobilních operačních systémech jsou vlákna plánována preemptivním multitaskingem: operační systém může provádění vlákna kdykoli přerušit a předat řízení jinému (context switch). Přepnutí kontextu je nákladná operace (1–10 mikrosekund), protože vyžaduje uložení/obnovení registrů CPU, aktualizaci TLB a vyčištění mezipamětí. Právě proto nadměrný počet vláken (stovky a tisíce) zhoršuje výkon — operační systém tráví více času přepínáním než prováděním.
Vlákno a proces jsou různé pojmy. Proces je instance aplikace s vyhrazenou virtuální pamětí. Vlákno uvnitř procesu sdílí tuto paměť s ostatními vlákny. V Androidu každá komponenta aplikace (Activity, Service, BroadcastReceiver) pracuje v jednom procesu, ale může běžet v různých vláknech. Aplikace iOS je také jedním procesem s možností vytvářet další vlákna přes GCD nebo Thread.
Každé vlákno v Java/Kotlin (Android) a NSThread (iOS) prochází pěti stavy: New (vytvořeno), Runnable (připraveno k provádění), Running (běží na CPU), Blocked/Waiting (čeká na zdroj nebo oznámení), Terminated (ukončeno). Přechody mezi stavy řídí plánovač operačního systému a synchronizační primitiva. Vývojář může ovlivnit prioritu vlákna (Thread.setPriority()) a jeho stav (sleep, join, interrupt).
V Androidu přechází vlákno do stavu Blocked při pokusu o získání obsazeného monitoru (synchronized), při volání Object.wait() nebo Thread.sleep(). V iOS — při volání NSCondition.wait(), pthread_cond_wait() nebo dispatch_semaphore_wait(). Ve stavu Blocked vlákno nespotřebovává CPU, ale zabírá paměť (zásobník). Vlákno může být přerušeno (interrupted) z jiného vlákna, obdrží InterruptedException (Java) nebo kontrolou isCancelled (Kotlin Coroutines).
| Stav | Popis | Metoda přechodu |
|---|---|---|
| New | Vlákno je vytvořeno, ale nebylo spuštěno | Konstruktor Thread() |
| Runnable | Vlákno je připraveno k provádění, čeká na CPU | thread.start() |
| Running | Vlákno běží na jádře CPU | Plánovač operačního systému |
| Blocked/Waiting | Vlákno čeká na zdroj, monitor nebo oznámení | synchronized, wait(), sleep() |
| Terminated | Vlákno dokončilo run() nebo bylo přerušeno | run() dokončeno, interrupt() |
Context switch (přepnutí kontextu) — operace, při které operační systém uloží stav aktuálního vlákna (registry, PC, TLB) a načte uložený stav jiného. V mobilních systémech (Linux + ART, XNU pro iOS) trvá context switch 1–10 mikrosekund. Pokud vlákno provádí úlohu za 100 mikrosekund a context switch trvá 5, ztrácí se 5 % času. Pro minimalizaci context switch používá iOS GCD s work stealing, Android — fondy s fixedThreadCount.
Android prošel evolucí od nízkoúrovňového java.lang.Thread po moderní korutiny. Každá úroveň abstrakce dává více možností s menší režií. Thread je základní třída, ale jeho přímé vytváření se nedoporučuje: nové vlákno není spravováno fondem, je obtížné ho sledovat a rušit. AsyncTask (deprecated od API 30) byl krokem vpřed, ale trpěl úniky paměti a nepohodlným zpracováním konfigurací.
HandlerThread — speciální podtřída Thread s Looper, která může zpracovávat frontu zpráv. Používá se pro sekvenční provádění úloh na vlákně na pozadí, například zápis dat do Room nebo souborů. HandlerThread se vytváří voláním start(), poté lze přes Handler(handlerThread.looper) odesílat zprávy a Runnable. Volání handlerThread.quit() zastaví Looper a ukončí vlákno.
// Android: Thread, HandlerThread a Executors
import android.os.Handler
import android.os.HandlerThread
import java.util.concurrent.Executors
class ThreadExample {
// 1. Přímé vytvoření Thread (nedoporučuje se)
fun directThread() {
val thread = Thread(Runnable {
Thread.sleep(1000)
print("Přímé vlákno vykonáno")
})
thread.start()
}
// 2. HandlerThread pro sekvenční úlohy na pozadí
fun handlerThreadExample() {
val handlerThread = HandlerThread("BackgroundQueue")
handlerThread.start()
val handler = Handler(handlerThread.looper)
handler.post {
// Sekvenční provádění na vlákně na pozadí
Thread.sleep(500)
print("HandlerThread: úloha dokončena")
}
// Zastavení vlákna (spustí se po dokončení úloh)
handlerThread.quitSafely()
}
// 3. Executors — fond vláken
fun executorExample() {
val executor = Executors.newFixedThreadPool(4)
for (i in 1..10) {
executor.execute {
print("Task $i on thread ${Thread.currentThread().getName()}")
}
}
executor.shutdown()
}
// 4. Kotlin Coroutines — moderní standard
suspend fun coroutineExample() = kotlinx.coroutines.withContext(
kotlinx.coroutines.Dispatchers.Default
) {
print("Coroutine on thread: ${Thread.currentThread().getName()}")
}
}
Příklad ThreadExample ukazuje všechny čtyři úrovně abstrakce vláken v Androidu. Přímé vytvoření Thread je nejnízkoúrovňovější a nejméně efektivní přístup. HandlerThread je užitečný pro sekvenční úlohy na pozadí. Executors.newFixedThreadPool(4) vytváří fond 4 vláken pro paralelní provádění až 10 úloh. Kotlin Coroutines s Dispatchers.Default — moderní, efektivní a bezpečný způsob.
HandlerThread — specializovaná podtřída Thread s vestavěným Looper a frontou zpráv. Vytváří se voláním start(), poté lze přes Handler(handlerThread.looper) odesílat Runnable a zprávy. HandlerThread provádí úlohy přísně sekvenčně — další úloha nezačne, dokud se nedokončí předchozí. To je vhodné pro zápis dat do Room nebo souborů, kde je pořadí operací kritické. Volání quitSafely() zastaví Looper po dokončení aktuální úlohy.
iOS také nabízí tři úrovně práce s vlákny. Thread (Thread ve Swift, NSThread v Objective-C) — nízkoúrovňové API, které přímo vytváří nativní vlákno. GCD (Grand Central Dispatch) přes DispatchQueue — hlavní nástroj pro vývojáře iOS, který automaticky spravuje fond vláken. OperationQueue — vysokoúrovňová abstrakce nad GCD s podporou závislostí, priorit a rušení.
Přímé použití Thread v moderním vývoji iOS je extrémně vzácné — GCD poskytuje všechny potřebné možnosti s automatickou správou paměti a vláken. Thread se používá pouze pro specifické případy: nastavení thread-local storage (threadDictionary), vytvoření RunLoop pro vlákno na pozadí nebo integrace s knihovnami C, které očekávají pthread_t.
import Foundation
class ThreadManager {
// 1. Thread (nízkoúrovňový)
func createThread() {
let thread = Thread {
// Kód běží na novém vlákně
print("Current thread: \(Thread.current)")
}
thread.name = "com.app.worker"
thread.qualityOfService = .utility
thread.start()
}
// 2. GCD — DispatchQueue
func gcdExample() {
// Paralelní fronta
let queue = DispatchQueue(label: "com.app.concurrent",
qos: .utility,
attributes: .concurrent)
queue.async {
print("Asynchronní úloha GCD")
}
// Barrier pro synchronizaci zápisu
queue.async(flags: .barrier) {
// Exkluzivní přístup během zápisu
print("Barrier write: exkluzivní přístup")
}
}
// 3. OperationQueue se závislostmi
func operationQueueExample() {
let queue = OperationQueue()
queue.maxConcurrentOperationCount = 2
queue.qualityOfService = .background
let download = BlockOperation {
print("Stahování...")
}
let process = BlockOperation {
print("Zpracování...")
}
let save = BlockOperation {
print("Ukládání...")
}
// Závislosti: download -> process -> save
process.addDependency(download)
save.addDependency(process)
queue.addOperations([download, process, save], waitUntilFinished: false)
}
}
// Thread-safe kolekce přes GCD barrier
class ThreadSafeArray<T> {
private var array: [T] = []
private let queue = DispatchQueue(label: "com.app.concurrent",
attributes: .concurrent)
var count: Int {
return queue.sync { array.count } // concurrent read
}
func append(_ element: T) {
queue.async(flags: .barrier) { // exclusive write
self.array.append(element)
}
}
}
Třída ThreadSafeArray demonstruje vzor Concurrent Read / Exclusive Write přes GCD barrier. Čtení přes queue.sync{} probíhá paralelně z více vláken. Zápis přes queue.async(flags: .barrier) blokuje všechny ostatní operace (čtení i zápis) do dokončení zápisu. To je efektivnější než bloky synchronized, protože neblokuje čtenáře, dokud neprobíhá zápis.
Přímé použití Thread v iOS je opodstatněné ve třech případech: pro thread-local storage (Thread.current.threadDictionary) — ukládání dat vázaných na vlákno; pro vytvoření speciálního RunLoop na vlákně na pozadí pomocí performSelector:onThread:; pro integraci s knihovnami C/C++, které očekávají pthread_t. Ve všech ostatních případech je preferováno GCD přes DispatchQueue — automaticky spravuje fond vláken a spotřebu energie.
Race condition (soutěžní stav) vzniká, když dvě a více vláken současně přistupují ke sdíleným datům a alespoň jedno z nich provádí zápis. Výsledek závisí na pořadí provádění (timing) a je nepředvídatelný. K prevenci race condition se používají synchronizační primitiva. V mobilním vývoji jsou k dispozici zámky (synchronized, NSLock), atomické operace (AtomicInteger, vlastnosti atomic v iOS) a fronty (serial queue).
Výběr primitiva závisí na scénáři. Pro jednoduché počítadla a příznaky stačí atomické operace (AtomicInteger, atomic property). Pro kritické sekce s více operacemi — zámky (synchronized, NSLock). Pro složité datové struktury — serial DispatchQueue nebo GCD barrier. Zámky jsou srozumitelnější, ale jsou náchylné k deadlockům a livelockům. Fronty jsou složitější, ale bezpečnější.
// Synchronizace v Android/Kotlin
import java.util.concurrent.atomic.AtomicInteger
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
class Counter {
// 1. AtomicInteger — pro jednoduchá počítadla
private val atomicCount = AtomicInteger(0)
fun incrementAtomic() = atomicCount.incrementAndGet()
// 2. synchronized — pro kritické sekce
@Synchronized
fun synchronizedOperation() {
// Vždy jen jedno vlákno
doWork()
}
// 3. Mutex z korutin — suspend-safe
private val mutex = Mutex()
suspend fun mutexOperation() {
mutex.withLock {
// protected code — thread-safe
doWork()
}
}
private fun doWork() { /* critical section */ }
}
// Příklad deadlock: A blokuje B, B blokuje A
class DeadlockExample {
private val lockA = Any()
private val lockB = Any()
fun methodA() = synchronized(lockA) {
Thread.sleep(100)
synchronized(lockB) { print("OK") }
}
fun methodB() = synchronized(lockB) {
Thread.sleep(100)
synchronized(lockA) { print("OK") }
}
}
Counter demonstruje tři přístupy k synchronizaci. AtomicInteger.incrementAndGet() — atomická operace bez zámků (CAS). @Synchronized — vestavěný monitor Java, blokuje celý objekt. Mutex.withLock — korutinový mutex, pozastavuje korutinu místo blokování vlákna (efektivnější). DeadlockExample ukazuje klasický deadlock: dvě vlákna získávají zámky v různém pořadí.
Thread Pool (fond vláken) — soubor předem vytvořených vláken, která se znovu používají pro provádění úloh. Místo vytváření nového vlákna pro každou úlohu (drahé) fond vezme volné vlákno z fondu. Pokud nejsou volná vlákna, úloha se zařadí do fronty. Fond automaticky spravuje velikost: nová vlákna se vytvářejí při špičkovém zatížení, neaktivní vlákna se ukončují. Tím se desetkrát snižuje režie vytváření vláken.
V Androidu Executors.newFixedThreadPool(4) vytváří fond 4 vláken. Pokud současně přijde 10 úloh, 4 začnou okamžitě a 6 bude čekat ve frontě. Executors.newCachedThreadPool() vytváří vlákna podle potřeby (bez limitu) a po 60 sekundách ukončuje neaktivní. Pro iOS GCD automaticky poskytuje fondy globálních front, jejichž velikost odpovídá počtu jader CPU a aktuálnímu zatížení.
V Kotlin Coroutines jsou fondy vláken skryté uvnitř dispatcherů. Dispatchers.Default používá fond velikosti rovnající se počtu jader CPU (minimálně 2). Dispatchers.IO — 64 vláken (dostatečné pro stovky úloh IO-bound, protože většina bude čekat na vstup-výstup, aniž by zabírala CPU). Každý dispatcher automaticky škáluje fond podle zatížení a šetří energii baterie v klidovém stavu.
Časté dotazy
Thread — základní jednotka provádění kódu v aplikaci. Každý proces může mít mnoho vláken, která sdílejí paměť, ale mají vlastní zásobník. V mobilním vývoji se vlákna používají pro paralelní provádění úloh bez blokování UI. Android používá Thread, Executors, HandlerThread a Coroutines. iOS používá Thread, GCD (DispatchQueue) a OperationQueue.
Vytvoření Thread vyžaduje alokaci ~1 MB pro zásobník v Androidu a ~512 KB v iOS — to je nákladná operace. Pro 1000 úloh by přímé vytvoření 1000 vláken vyžadovalo ~1 GB jen na zásobníky plus režii na context switch. Místo Thread používejte fondy (Executors, GCD) nebo korutiny — znovu používají vlákna a desetkrát snižují režii.
Race condition — nepředvídatelné chování při současném přístupu více vláken ke sdíleným datům se zápisem. Lze se jí vyhnout třemi způsoby: používat atomické typy (AtomicInteger), zámky (synchronized, NSLock) nebo serializovat přístup přes frontu (serial DispatchQueue, Actor v Kotlinu). Nejlepší praxe — minimalizovat sdílený mutable stav a používat immutability.
Thread — nativní systémový objekt, který zabírá ~1 MB zásobníku a je vázán na jádro operačního systému. Korutina — lehká jednotka provádění v Kotlinu, která není vázána na konkrétní vlákno a může být pozastavena (suspend) bez blokování. Jedno vlákno může provádět tisíce korutin. Korutiny jsou paměťově efektivnější a umožňují psát asynchronní kód bez callbacks.
Deadlock se projevuje jako úplné zamrznutí aplikace bez ANR. V Androidu použijte Thread.getAllStackTraces() pro výpis zásobníků všech vláken — dvě vlákna budou čekat na zámky toho druhého. V iOS — Thread.callStackSymbols. Nástroje: Android Studio Profiler (karta Threads), Instruments (iOS, Thread State View). Prevence: zámky získávejte v pevném pořadí, používejte tryLock s časovým limitem.
Závěr
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také