Concorrência e Multithreading em Kotlin: Nível Avançado
\nNo desenvolvimento moderno com Kotlin, especialmente para aplicações server-side e Android, o gerenciamento adequado de concorrência é a chave para desempenho e responsividade. No nível avançado, não basta apenas iniciar uma corrotina. É necessário entender como sincronizar eficientemente o acesso a dados, gerenciar threads e evitar bloqueios "infernais". Neste artigo, abordaremos técnicas avançadas: desde os detalhes de kotlinx.coroutines até coleções thread-safe e canais.
1. Mergulho Profundo em Corrotinas: Structured Concurrency e Scopes
\nA base do trabalho avançado é a Structured Concurrency. Este é o princípio onde o ciclo de vida de uma corrotina está vinculado a um escopo (scope). Se a corrotina pai for cancelada, todas as suas tarefas filhas são automaticamente encerradas. Isso evita vazamentos de memória e processos incontroláveis.
\nNo nível avançado, você deve criar seus próprios scopes, em vez de depender do GlobalScope global.
import kotlinx.coroutines.*\nimport kotlin.coroutines.CoroutineContext\n\nclass DataRepository {\n // Scope próprio com dispatcher e manipulador de exceções\n private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob() + CoroutineExceptionHandler { _, throwable ->\n println("Erro não tratado: ${throwable.message}")\n })\n\n fun loadData() {\n scope.launch {\n // corrotinas filhas\n val data = async { fetchFromNetwork() }\n val cache = async { loadFromCache() }\n process(data.await(), cache.await())\n }\n }\n\n fun cleanup() {\n scope.cancel() // cancela todas as corrotinas no escopo\n }\n}\n\nUse SupervisorJob quando a falha de uma corrotina filha não deve cancelar as demais. Isso é crítico para camadas de UI ou requisições paralelas independentes.
2. Coleções Thread-Safe e Operações Atômicas
\nMesmo com corrotinas, se você trabalha com dados mutáveis compartilhados, a sincronização é necessária. Kotlin oferece várias abordagens:
\n- \n
- Tipos atômicos (
AtomicInteger,AtomicReference) — para contadores e flags simples. \n - Coleções Thread-Safe (
ConcurrentHashMap,CopyOnWriteArrayList) — para estruturas complexas. \n - Mutex do
kotlinx.coroutines— para seções críticas dentro de corrotinas. \n
import kotlinx.coroutines.*\nimport kotlinx.coroutines.sync.Mutex\nimport kotlinx.coroutines.sync.withLock\nimport java.util.concurrent.ConcurrentHashMap\nimport java.util.concurrent.atomic.AtomicInteger\n\nclass SafeCounter {\n private val mutex = Mutex()\n private var counter = 0\n\n suspend fun increment() {\n mutex.withLock {\n counter++ // seção crítica\n }\n }\n\n fun getCounter(): Int = counter\n}\n\nclass CacheManager {\n private val cache = ConcurrentHashMap<String, String>()\n private val hitCount = AtomicInteger(0)\n\n fun put(key: String, value: String) {\n cache[key] = value\n }\n\n fun get(key: String): String? {\n val result = cache[key]\n if (result != null) hitCount.incrementAndGet()\n return result\n }\n}\n\nImportante: Mutex é uma função suspend, portanto não bloqueia a thread, mas suspende a corrotina. Isso é mais eficiente que synchronized.
3. Canais (Channels) e Flow: Fluxos de Dados Reativos
\nPara transferir dados entre corrotinas, use Channels (análogo ao BlockingQueue para corrotinas) e Flow (fluxo assíncrono frio).
Canais: Producer-Consumer
\nimport kotlinx.coroutines.channels.*\nimport kotlinx.coroutines.*\n\nfun Corout