Boas práticas e padrões · Concorrência · 5 min de leitura
Condições de corrida e imutabilidade
Uma condição de corrida acontece quando duas tarefas leem e escrevem o mesmo dado ao mesmo tempo e o resultado depende de quem chega primeiro. Imutabilidade e confinamento evitam o problema na raiz.
Arraste para o lado para ver o diagrama inteiro.
O problema
contador++ parece uma operação, mas são três: ler, somar, escrever. Com duas threads (ou duas corrotinas em threads diferentes), uma escrita pode apagar a outra — o bug aparece de vez em quando, some no debugger e volta em produção. O mesmo vale para dois usuários editando o mesmo registro no servidor.
Soluções, da melhor para a pior
- Imutabilidade: objetos que não mudam podem ser lidos por qualquer um. Crie um novo valor em vez de alterar (
copy(),val,struct,readonly). - Confinamento: só uma thread ou um actor mexe no estado (Main,
actor,Dispatchers.Main). - Operações atômicas:
MutableStateFlow.update { },AtomicInteger,UPDATE … SET saldo = saldo + 50no banco. - Locks (
Mutex,synchronized): funcionam, mas é fácil esquecer ou travar (deadlock).
No banco de dados
Duas requisições lendo e gravando o mesmo registro também correm. Use atualização atômica no SQL, transação com o nível de isolamento adequado ou controle otimista (coluna de versão: só grava se a versão não mudou — o HTTP 412 existe para isso).
No código: antes e depois
class Carrinho {
var itens = mutableListOf<Item>()
var total = 0.0
fun adicionar(i: Item) { // chamado de várias corrotinas em Dispatchers.Default
itens.add(i) // ArrayList não é thread-safe
total += i.preco // ler + somar + escrever: corrida
}
} data class Carrinho(val itens: List<Item> = emptyList()) {
val total get() = itens.sumOf { it.preco } // derivado, nunca fora de sincronia
}
class CarrinhoStore {
private val _estado = MutableStateFlow(Carrinho())
val estado = _estado.asStateFlow()
// update {} tenta de novo se outro escreveu no meio (compare-and-set)
fun adicionar(i: Item) = _estado.update { it.copy(itens = it.itens + i) }
} Estado imutável + update atômico: não há como duas adições se perderem, e o total é sempre calculado a partir dos itens.
Em resumo
- Ler-calcular-escrever não é atômico.
- Prefira dados imutáveis.
- Confine o estado a uma thread ou actor.
- No banco: update atômico ou versão.
Perguntar à IA
Monte um prompt pronto sobre Condições de corrida e imutabilidade na sua linguagem e no seu caso, e abra num chat gratuito.
O prompt vai para o site que você escolher. Não cole senhas, tokens nem dados reais de clientes. Para Gemini e outros, copie e cole.