Thread no desenvolvimento mobile — o que é, tipos e gerenciamento de threads

Autor: IT Sectr Publicado: 2026-03-16 Tempo de leitura: 11 min

Thread — é a unidade básica de tempo de processador, com sua própria pilha e executando-se independentemente de outras threads. No desenvolvimento mobile, threads são usadas para execução paralela de tarefas, mantendo a interface responsiva durante operações longas. Android suporta java.lang.Thread, Executors e Kotlin Coroutines, iOS — Thread (Objective-C), GCD e OperationQueue. De acordo com Android Thread Documentation, a criação de uma thread nativa exige a alocação de ~1 MB para a pilha pelo sistema operacional.

Principais pontos

  • Thread — unidade mínima de agendamento da CPU: cada thread é independente e tem sua própria pilha
  • Criação de thread requer ~1 MB de pilha no Android e 512 KB no iOS, portanto pools são mais eficientes que criação direta
  • Android: Thread, Executors, HandlerThread, Coroutines — quatro níveis de abstração de threads
  • iOS: Thread (baixo nível), GCD (DispatchQueue), OperationQueue (alto nível)
  • Thread safety — acesso compartilhado a dados mutáveis requer sincronização: locks, atomic, serial queues

O que é Thread

Thread (linha de execução) — é uma sequência independente de instruções que o sistema operacional pode agendar em um núcleo da CPU. Cada processo (aplicativo) contém pelo menos uma thread — a Main Thread. Threads adicionais são criadas para execução paralela de tarefas. Cada thread tem sua própria pilha de programa (com variáveis locais), contador de programa (PC) e registradores. A memória heap é compartilhada entre todas as threads do processo.

Em sistemas operacionais móveis, as threads são agendadas por multitarefa preemptiva (preemptive multitasking): o SO pode interromper a execução de uma thread a qualquer momento e passar o controle para outra (context switch). A alternância de contexto é uma operação cara (1-10 microssegundos), pois requer salvar/restaurar registradores da CPU, atualizar o TLB e limpar caches. É por isso que um número excessivo de threads (centenas e milhares) prejudica o desempenho — o SO gasta mais tempo alternando do que executando.

Thread e processo — são conceitos diferentes. Processo é uma instância do aplicativo com memória virtual dedicada. Uma thread dentro do processo compartilha essa memória com outras threads. No Android, cada componente do aplicativo (Activity, Service, BroadcastReceiver) funciona em um processo, mas pode ser executado em threads diferentes. O aplicativo iOS também é um único processo com capacidade de criar threads adicionais via GCD ou Thread.

Ciclo de vida da thread: estados e transições

Cada thread em Java/Kotlin (Android) e NSThread (iOS) passa por cinco estados: New (criada), Runnable (pronta para execução), Running (executando na CPU), Blocked/Waiting (aguardando recurso ou notificação), Terminated (finalizada). As transições entre estados são gerenciadas pelo agendador do SO e por primitivas de sincronização. O desenvolvedor pode influenciar a prioridade da thread (Thread.setPriority()) e seu estado (sleep, join, interrupt).

No Android, a thread entra no estado Blocked ao tentar capturar um monitor ocupado (synchronized), chamar Object.wait() ou Thread.sleep(). No iOS — ao chamar NSCondition.wait(), pthread_cond_wait() ou dispatch_semaphore_wait(). No estado Blocked, a thread não consome CPU, mas ocupa memória (pilha). A thread pode ser interrompida (interrupted) de outra thread, recebendo InterruptedException (Java) ou isCancelled (Kotlin Coroutines).

EstadoDescriçãoMétodo de transição
NewThread criada, mas não iniciadaConstrutor Thread()
RunnableThread pronta para execução, aguardando CPUthread.start()
RunningThread executando no núcleo da CPUAgendador do SO
Blocked/WaitingThread aguardando recurso, monitor ou notificaçãosynchronized, wait(), sleep()
TerminatedThread finalizou run() ou foi interrompidarun() concluído, interrupt()

Context Switch e seu custo

Context switch (alternância de contexto) — operação na qual o SO salva o estado da thread atual (registradores, PC, TLB) e carrega o estado salvo de outra. Em sistemas móveis (Linux + ART, XNU para iOS) o context switch leva de 1 a 10 microssegundos. Se uma thread executa uma tarefa em 100 microssegundos e o context switch leva 5, então 5% do tempo é desperdiçado. Para minimizar o context switch, o iOS usa GCD com work stealing, o Android — pools com fixedThreadCount.

Thread no Android: de Thread a Coroutines

Android evoluiu do java.lang.Thread de baixo nível para as coroutines modernas. Cada nível de abstração oferece mais capacidades com menos sobrecarga. Thread — classe básica, mas sua criação direta não é recomendada: a nova thread não é gerenciada por pool, é difícil de monitorar e cancelar. AsyncTask (deprecated desde API 30) foi um passo à frente, mas sofria de vazamentos de memória e tratamento inconveniente de configurações.

HandlerThread — uma subclasse especial de Thread com Looper, que pode processar uma fila de mensagens. É usado para execução sequencial de tarefas em uma thread de fundo, como escrever dados no Room ou em arquivos. HandlerThread é criado chamando start(), após o qual via Handler(handlerThread.looper) é possível enviar mensagens e Runnable. A chamada handlerThread.quit() para o Looper e finaliza a thread.

kotlin
// Android: Thread, HandlerThread e Executors
import android.os.Handler
import android.os.HandlerThread
import java.util.concurrent.Executors

class ThreadExample {

    // 1. Criação direta de Thread (não recomendada)
    fun directThread() {
        val thread = Thread(Runnable {
            Thread.sleep(1000)
            print("Direct thread executed")
        })
        thread.start()
    }

    // 2. HandlerThread para tarefas sequenciais em segundo plano
    fun handlerThreadExample() {
        val handlerThread = HandlerThread("BackgroundQueue")
        handlerThread.start()

        val handler = Handler(handlerThread.looper)
        handler.post {
            // Execução sequencial em thread de fundo
            Thread.sleep(500)
            print("HandlerThread: tarefa concluída")
        }

        // Parar a thread (executado quando as tarefas terminam)
        handlerThread.quitSafely()
    }

    // 3. Executors — pool de threads
    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 — padrão moderno
    suspend fun coroutineExample() = kotlinx.coroutines.withContext(
        kotlinx.coroutines.Dispatchers.Default
    ) {
        print("Coroutine on thread: ${Thread.currentThread().getName()}")
    }
}

O exemplo ThreadExample mostra todos os quatro níveis de abstração de threads no Android. A criação direta de Thread — a abordagem mais baixo nível e ineficiente. HandlerThread é útil para tarefas sequenciais em segundo plano. Executors.newFixedThreadPool(4) cria um pool de 4 threads para execução paralela de até 10 tarefas. Kotlin Coroutines com Dispatchers.Default — a maneira moderna, eficiente e segura.

HandlerThread: tarefas sequenciais em segundo plano

HandlerThread — uma subclasse especializada de Thread com Looper integrado e fila de mensagens. Ele é criado chamando start(), após o qual via Handler(handlerThread.looper) é possível enviar Runnable e mensagens. HandlerThread executa tarefas estritamente sequenciais — a próxima tarefa não começa até a anterior ser concluída. Isso é conveniente para escrever dados no Room ou em arquivos, onde a ordem das operações é crítica. A chamada quitSafely() para o Looper após a conclusão da tarefa atual.

Thread no iOS: Thread, GCD e OperationQueue

iOS também oferece três níveis de trabalho com threads. Thread (Thread em Swift, NSThread em Objective-C) — API de baixo nível que cria diretamente uma thread nativa. GCD (Grand Central Dispatch) via DispatchQueue — a principal ferramenta para desenvolvedores iOS, que gerencia automaticamente o pool de threads. OperationQueue — abstração de alto nível sobre GCD com suporte a dependências, prioridades e cancelamento.

O uso direto de Thread no desenvolvimento iOS moderno é extremamente raro — o GCD fornece todas as capacidades necessárias com gerenciamento automático de memória e threads. Thread é usado apenas para casos específicos: configurar armazenamento thread-local (threadDictionary), criar RunLoop para thread de fundo ou integração com bibliotecas C que esperam pthread_t.

swift
import Foundation

class ThreadManager {

    // 1. Thread (baixo nível)
    func createThread() {
        let thread = Thread {
            // Código executado em nova thread
            print("Current thread: \(Thread.current)")
        }
        thread.name = "com.app.worker"
        thread.qualityOfService = .utility
        thread.start()
    }

    // 2. GCD — DispatchQueue
    func gcdExample() {
        // Fila paralela
        let queue = DispatchQueue(label: "com.app.concurrent",
                                 qos: .utility,
                                 attributes: .concurrent)

        queue.async {
            print("GCD async task")
        }

        // Barrier para sincronização de escrita
        queue.async(flags: .barrier) {
            // Acesso exclusivo durante escrita
            print("Barrier write: exclusive access")
        }
    }

    // 3. OperationQueue com dependências
    func operationQueueExample() {
        let queue = OperationQueue()
        queue.maxConcurrentOperationCount = 2
        queue.qualityOfService = .background

        let download = BlockOperation {
            print("Downloading...")
        }
        let process = BlockOperation {
            print("Processing...")
        }
        let save = BlockOperation {
            print("Saving...")
        }

        // Dependências: download -> process -> save
        process.addDependency(download)
        save.addDependency(process)

        queue.addOperations([download, process, save], waitUntilFinished: false)
    }
}

// Coleção thread-safe via 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)
        }
    }
}

A classe ThreadSafeArray demonstra o padrão Concurrent Read / Exclusive Write via GCD barrier. A leitura via queue.sync{} é executada paralelamente de várias threads. A escrita via queue.async(flags: .barrier) bloqueia todas as outras operações (leitura e escrita) até a conclusão da escrita. Isso é mais eficiente que blocos synchronized, pois não bloqueia leitores enquanto não há escrita.

iOS Thread vs GCD: quando usar Thread diretamente

O uso direto de Thread no iOS é justificado em três casos: para armazenamento thread-local (Thread.current.threadDictionary) — armazenamento de dados vinculados à thread; para criar um RunLoop especial em thread de fundo com performSelector:onThread:; para integração com bibliotecas C/C++ que esperam pthread_t. Em todos os outros casos, o GCD via DispatchQueue é preferível — ele gerencia automaticamente o pool de threads e o consumo de energia.

Sincronização de threads: locks, atomic, serial queues

Race condition (condição de corrida) ocorre quando duas ou mais threads acessam simultaneamente dados compartilhados, e pelo menos uma das threads está escrevendo. O resultado depende da ordem de execução (timing) e é imprevisível. Para prevenir race condition, são usadas primitivas de sincronização. No desenvolvimento mobile, estão disponíveis bloqueios (synchronized, NSLock), operações atômicas (AtomicInteger, propriedades atomic do iOS) e filas (serial queue).

A escolha da primitiva depende do cenário. Para contadores e flags simples, operações atômicas (AtomicInteger, atomic property) são suficientes. Para seções críticas com múltiplas operações — bloqueios (synchronized, NSLock). Para estruturas de dados complexas — serial DispatchQueue ou GCD barrier. Bloqueios são mais fáceis de entender, mas são suscetíveis a deadlocks e livelocks. Filas são mais complexas, porém mais seguras.

kotlin
// Sincronização em Android/Kotlin
import java.util.concurrent.atomic.AtomicInteger
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock

class Counter {

    // 1. AtomicInteger — para contadores simples
    private val atomicCount = AtomicInteger(0)
    fun incrementAtomic() = atomicCount.incrementAndGet()

    // 2. synchronized — para seções críticas
    @Synchronized
    fun synchronizedOperation() {
        // Apenas uma thread por vez
        doWork()
    }

    // 3. Mutex de coroutines — suspend-safe
    private val mutex = Mutex()
    suspend fun mutexOperation() {
        mutex.withLock {
            // código protegido — thread-safe
            doWork()
        }
    }

    private fun doWork() { /* critical section */ }
}

// Exemplo de Deadlock: A bloqueia B, B bloqueia 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 demonstra três abordagens para sincronização. AtomicInteger.incrementAndGet() — operação atômica sem bloqueios (CAS). @Synchronized — monitor embutido do Java, bloqueia o objeto inteiro. Mutex.withLock — mutex de coroutine, suspende a coroutine em vez de bloquear a thread (mais eficiente). DeadlockExample mostra um deadlock clássico: duas threads capturam bloqueios em ordem diferente.

Thread Pools: por que Executors são melhores que Thread

Thread Pool (pool de threads) — é um conjunto de threads pré-criadas que são reutilizadas para executar tarefas. Em vez de criar uma nova thread para cada tarefa (caro), o pool pega uma thread livre do pool. Se não houver threads livres, a tarefa é colocada em uma fila. O pool gerencia automaticamente o tamanho: novas threads são criadas sob carga de pico, threads ociosas são finalizadas. Isso reduz a sobrecarga de criação de threads dezenas de vezes.

No Android, Executors.newFixedThreadPool(4) cria um pool de 4 threads. Se 10 tarefas chegarem simultaneamente, 4 começam a execução imediatamente, 6 aguardam na fila. Executors.newCachedThreadPool() cria threads conforme necessário (sem limite) e finaliza as ociosas após 60 segundos. Para iOS, o GCD fornece automaticamente pools de filas globais, cujo tamanho corresponde ao número de núcleos da CPU e à carga atual.

Em Kotlin Coroutines, os pools de threads estão ocultos dentro dos dispatchers. Dispatchers.Default usa um pool de tamanho igual ao número de núcleos da CPU (mínimo 2). Dispatchers.IO — 64 threads (suficiente para centenas de tarefas IO-bound, já que a maioria aguardará entrada-saída, sem ocupar CPU). Cada dispatcher dimensiona automaticamente o pool conforme a carga, economizando bateria durante a inatividade.

Perguntas frequentes

O que é Thread no desenvolvimento mobile?

Thread — é a unidade básica de execução de código em um aplicativo. Cada processo pode ter múltiplas threads, compartilhando memória, mas com sua própria pilha. No desenvolvimento mobile, threads são usadas para execução paralela de tarefas sem bloquear a UI. Android usa Thread, Executors, HandlerThread e Coroutines. iOS usa Thread, GCD (DispatchQueue) e OperationQueue.

Por que não é recomendado criar Thread diretamente?

Criar Thread requer alocação de ~1 MB de pilha no Android e ~512 KB no iOS — é uma operação cara. Para 1000 tarefas, a criação direta de 1000 threads exigiria ~1 GB apenas para as pilhas, mais a sobrecarga de context switch. Em vez de Thread, use pools (Executors, GCD) ou coroutines — eles reutilizam threads, reduzindo a sobrecarga dezenas de vezes.

O que é race condition e como evitá-la?

Race condition — comportamento imprevisível quando múltiplas threads acessam simultaneamente dados compartilhados com escrita. Pode ser evitada de três formas: usar tipos atômicos (AtomicInteger), bloqueios (synchronized, NSLock) ou serializar o acesso através de uma fila (DispatchQueue serial, Actor em Kotlin). A melhor prática é minimizar o estado mutável compartilhado e usar imutabilidade.

Qual a diferença entre Thread e coroutine?

Thread — é um objeto nativo do sistema, ocupando ~1 MB de pilha e vinculado a um núcleo do SO. Coroutine — é uma unidade de execução leve do Kotlin, que não está vinculada a uma thread específica e pode ser suspensa (suspend) sem bloqueio. Uma única thread pode executar milhares de coroutines. Coroutines são mais eficientes em memória e permitem escrever código assíncrono sem callbacks.

Como detectar deadlock em um aplicativo mobile?

Deadlock se manifesta como um travamento completo do aplicativo sem ANR. No Android, use Thread.getAllStackTraces() para obter um dump das pilhas de todas as threads — duas threads estarão esperando pelos bloqueios uma da outra. No iOS — Thread.callStackSymbols. Ferramentas: Android Studio Profiler (aba Threads), Instruments (iOS, Thread State View). Prevenção: capture bloqueios em ordem fixa, use tryLock com timeout.

Resumo

  • Thread — unidade mínima da CPU: execução independente com pilha própria, memória heap compartilhada
  • Cinco estados da thread: New, Runnable, Running, Blocked/Waiting, Terminated
  • Android evoluiu: Thread → AsyncTask → Executors → HandlerThread → Coroutines
  • iOS oferece: Thread, GCD (DispatchQueue) e OperationQueue — do baixo ao alto nível
  • Race condition é resolvida com bloqueios (synchronized, NSLock), tipos atômicos e filas serial
  • Deadlock ocorre por captura cruzada de bloqueios — é prevenido por ordem fixa
  • Thread Pool é mais eficiente que criar novas Threads: reutiliza threads, reduz overhead de context switch

Vamos desenvolver um aplicativo móvel chave na mão

A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.

Discutir o projeto

Leia também