Concorrência e Multithreading em Kotlin: do Básico ao Avançado
Os aplicativos modernos cada vez mais exigem a execução de várias tarefas simultaneamente. Em Kotlin, assim como em outras linguagens JVM, o multithreading é tradicionalmente implementado através da classe Thread. No entanto, a linguagem oferece uma ferramenta muito mais poderosa e elegante — as corrotinas (coroutines). Neste artigo, abordaremos todos os aspectos de concorrência e multithreading em Kotlin: desde conceitos básicos até técnicas avançadas.
1. Fundamentos do Multithreading: Thread e Runnable
Vamos começar com a abordagem clássica. Em Kotlin, assim como em Java, é possível criar uma thread diretamente:
fun main() { val thread = Thread { for (i in 1..5) { println("Thread: $i") Thread.sleep(500) } } thread.start() println("Thread principal finalizada")}No entanto, o gerenciamento manual de threads tem desvantagens:
- Alto custo operacional — criar e destruir threads é caro.
- Complexidade de sincronização — é fácil cometer race condition.
- Número limitado de threads — o sistema pode criar apenas um número limitado de threads.
Para sincronizar o acesso a dados compartilhados, utiliza-se a palavra-chave synchronized ou bloqueios ReentrantLock:
class Counter { private var count = 0 @Synchronized fun increment() { count++ }}2. Corrotinas — abordagem moderna para concorrência
Corrotinas são threads leves que resolvem os problemas do multithreading clássico. Elas não estão vinculadas a threads do sistema e podem ser executadas em uma única thread, alternando entre tarefas.
2.1. Primeira corrotina
Para trabalhar com corrotinas, é necessário adicionar a dependência org.jetbrains.kotlinx:kotlinx-coroutines-core. Exemplo de uma corrotina simples:
import kotlinx.coroutines.*
fun main() = runBlocking { launch { delay(1000L) println("Mundo!") } println("Olá,")}Aqui, runBlocking é a ponte entre o código comum e as corrotinas. launch inicia uma nova corrotina, e delay é o análogo de Thread.sleep(), mas sem bloquear a thread.
2.2. Funções Suspend
A palavra-chave suspend permite pausar a execução de uma função sem bloquear a thread:
suspend fun fetchData(): String { delay(2000L) // simula uma requisição de rede return "Dados recebidos"}
fun main() = runBlocking { val result = async { fetchData() } println("Aguardando...") println(result.await())}2.3. Concorrência estruturada
Kotlin suporta concorrência estruturada — todas as corrotinas filhas são automaticamente concluídas antes da corrotina pai:
fun main() = runBlocking { coroutineScope { launch { delay(1000L) println("Tarefa 1") } launch { delay(500L) println("Tarefa 2") } } println("Todas as tarefas concluídas")}3. Técnicas avançadas: Channel, Flow e Dispatchers
3.1. Canais (Channel)
Canais permitem a troca de dados entre corrotinas. São análogos a uma fila:
fun main() = runBlocking { val channel = Channel<Int>() launch { for (i in 1..5) { channel.send(i * i) } channel.close() } for (value in channel) { println(value) }}3.2. Flow — fluxos de dados frios
Flow é um fluxo reativo que emite valores sob demanda:
fun simpleFlow(): Flow<Int> = flow { for (i in 1..3) { delay(