O que é synchronized e como utilizá-lo?
O synchronized
em Java é um mecanismo que garante que apenas uma thread por vez execute um determinado bloco de código ou método, evitando problemas de concorrência como Race Conditions.
1. Por que usar synchronized
?
Quando múltiplas threads acessam um recurso compartilhado, elas podem modificar dados de forma inconsistente. O synchronized
resolve isso garantindo exclusão mútua.
2. Usando synchronized
em métodos
O uso mais comum de synchronized
é aplicá-lo diretamente a um método:
class Contador {
private int valor = 0;
public synchronized void incrementar() {
valor++;
}
public int getValor() {
return valor;
}
}
Explicação:
- O método
incrementar()
agora só pode ser executado por uma thread por vez.
3. Usando synchronized
em blocos de código
Podemos sincronizar apenas partes críticas do código:
class Contador {
private int valor = 0;
public void incrementar() {
synchronized (this) {
valor++;
}
}
}
Vantagem: Bloquear apenas a parte necessária melhora o desempenho da aplicação.
4. Sincronizando com objetos específicos
Podemos sincronizar usando um objeto como lock compartilhado:
class Contador {
private int valor = 0;
private final Object lock = new Object();
public void incrementar() {
synchronized (lock) {
valor++;
}
}
}
Útil quando várias instâncias compartilham o mesmo bloqueio.
5. Problemas do synchronized
- Deadlock: Se duas threads ficarem bloqueadas esperando uma ação da outra.
- Baixo desempenho: Pode reduzir a escalabilidade em sistemas de alto desempenho.
- Não funciona para operações atômicas: Para isso, prefira
AtomicInteger
.
Conclusão
O synchronized
é uma ferramenta essencial para controle de concorrência em Java, garantindo a integridade dos dados ao impedir que múltiplas threads modifiquem um recurso ao mesmo tempo. No entanto, deve ser usado com cautela para evitar problemas de desempenho e deadlocks.
Quando usar synchronized para controle de concorrência?
A sincronização é um dos desafios mais complexos na programação multithread. Em sistemas críticos, como bancos de dados e transações financeiras, garantir que apenas uma thread execute determinada operação evita falhas graves. O uso do synchronized
é fundamental para resolver esses problemas, mas, para sistemas de alto desempenho, alternativas como ReentrantLock
e AtomicInteger
podem ser mais eficientes.
Algumas aplicações:
- Garantir a integridade de dados em sistemas multithread
- Evitar Race Conditions em operações críticas
- Controle de acesso a recursos compartilhados
- Sincronização de operações bancárias e transações financeiras
Dicas para quem está começando
- Use
synchronized
apenas quando necessário para evitar impacto no desempenho - Prefira sincronizar blocos de código ao invés de métodos inteiros
- Evite deadlocks garantindo que os bloqueios sejam liberados corretamente
- Para variáveis compartilhadas, considere
AtomicInteger
como alternativa
Contribuições de Rodrigo Farias