Synchronized: o que é, princípio de funcionamento e uso em Java

Autor: IT Sectr Publicado: 2026-03-19 Tempo de leitura: 8 min

Synchronized é um mecanismo de sincronização integrado na linguagem Java que fornece acesso exclusivo a secções críticas de código. De acordo com Oracle, 2024, o modificador synchronized garante que apenas uma thread pode executar o método ou bloco marcado num momento específico. Este mecanismo baseia-se em monitores — um conceito fundamental dos sistemas operativos que assegura o correto funcionamento de aplicações multithread de todos os níveis de complexidade.

Pontos principais

  • Synchronized — palavra-chave Java para acesso thread-safe a dados.
  • Monitor de objeto — o mecanismo interno no qual a sincronização se baseia.
  • Método synchronized bloqueia todo o método ao nível da instância ou classe.
  • Bloco synchronized permite sincronizar apenas parte do código.
  • Deadlock — um dos principais problemas com sincronização aninhada.

O que é synchronized?

Synchronized é uma palavra-chave em Java que garante que apenas uma thread executa uma secção protegida de código de cada vez, prevenindo a corrupção de dados durante o acesso concorrente. Apareceu na primeira versão do Java e continua a ser a forma mais simples de garantir thread safety para programadores de qualquer nível.

Definição e papel em Java

O modificador synchronized resolve duas tarefas: exclusão mútua e visibilidade de alterações. Quando uma thread sai de um bloco synchronized, todas as alterações são garantidamente visíveis para outras threads que entrem num bloco sincronizado no mesmo objeto.

Synchronized pode ser aplicado a um método inteiro ou a um bloco de código arbitrário especificando um objeto monitor. Em ambos os casos, a JVM insere instruções monitorenter e monitorexit ao nível do bytecode.

Razões para o seu surgimento

Em aplicações multithread sem sincronização, ocorre uma condição de corrida (race condition) quando duas threads modificam simultaneamente os mesmos dados, levando a resultados imprevisíveis. Synchronized tornou-se a primeira e principal ferramenta do Java para combater este problema, fornecendo uma sintaxe declarativa simples acessível a qualquer programador.

Como funciona o synchronized?

O mecanismo synchronized baseia-se no conceito de monitor — um primitivo de sincronização de alto nível incorporado em cada objeto Java. O monitor é associado a um objeto quando um bloco synchronized é usado pela primeira vez sobre ele.

Monitor de objeto

Cada objeto em Java tem um monitor associado. Quando uma thread entra num bloco synchronized, adquire o monitor do objeto. Se o monitor já estiver ocupado por outra thread, a thread bloqueia até ser libertado. Em bytecode, isto corresponde ao par de instruções monitorenter e monitorexit.

Estados de bloqueio (biased locking)

A JVM otimiza o synchronized através de vários níveis: biased locking (bloqueio tendencioso) para acesso de thread única, lightweight locking (bloqueio leve) para baixa contenção, e heavyweight locking (bloqueio pesado) para contenção intensa com envolvimento do SO. Estes níveis melhoram o desempenho sem alterar o código.

java
class Counter {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public synchronized int getCount() {
        return count;
    }
}

Regra happens-before

Synchronized estabelece uma relação happens-before: todas as ações numa thread antes de sair de um bloco synchronized são visíveis para outra thread depois de entrar num bloco sincronizado no mesmo objeto. Isto garante não apenas exclusão mútua mas também consistência de dados para todas as threads.

Método synchronized vs bloco

Java oferece duas formas de aplicar synchronized: ao nível do método e ao nível do bloco. A escolha entre eles afeta o desempenho e a granularidade da sincronização.

Método synchronized

Marcar um método com o modificador synchronized sincroniza-o automaticamente na instância atual (para métodos de instância) ou no objeto Class (para métodos estáticos). É a forma mais simples de garantir exclusão mútua, mas é muitas vezes excessiva se a secção crítica constituir apenas uma pequena parte do método e o resto do código não precisar de sincronização.

Bloco synchronized

Um bloco synchronized dá controlo preciso: especifica o objeto monitor e sincroniza apenas a secção necessária do código, deixando o resto do método fora do bloqueio. Isto minimiza o tempo de retenção do monitor e melhora o desempenho geral da aplicação num ambiente multithread, pois outras threads podem executar código não relacionado em paralelo sem esperar pela libertação do monitor.

java
class DataProcessor {
    private final Object lock = new Object();

    public void process() {
        // código fora da secção crítica - sem sincronização
        prepareData()

        synchronized (lock) {
            // apenas este bloco está protegido
            updateSharedState()
        }

        // continuação sem bloqueio
        cleanup()
    }
}
CritérioMétodo synchronizedBloco synchronized
Monitorthis (instância) ou Classqualquer objeto
Granularidademétodo inteiroapenas o código necessário
Legibilidadealtamédia
Desempenhomenor para métodos grandesmaior para secções críticas pequenas

Synchronized no Android

No desenvolvimento Android, synchronized é amplamente usado para proteger SharedPreferences, acesso a bases de dados e componentes de UI. No entanto, a sua utilização na thread principal é fortemente desaconselhada devido ao risco de congelamento da interface.

Uso com SharedPreferences

SharedPreferences no Android fornece thread safety básica, mas ao editar a partir de múltiplas threads através do Editor, pode ser necessária sincronização externa. Um bloco synchronized com um objeto de bloqueio separado garante consistência das alterações.

kotlin
class PreferencesManager(private val prefs: SharedPreferences) {
    private val lock = Any()

    fun writeToken(token: String) {
        synchronized (lock) {
            prefs.edit()
                .putString("auth_token", token)
                .apply()
        }
    }
}

Limitações em aplicações Android

A principal limitação do synchronized no Android é o bloqueio de thread. Ao contrário das corrotinas com Mutex, o synchronized bloqueia a thread do sistema por completo. Na thread principal, isto causa ANR. No desenvolvimento Android moderno, recomenda-se substituir synchronized por corrotinas (suspend Mutex) ou tipos atómicos (AtomicInteger).

Alternativas ao synchronized

O Java e Kotlin modernos oferecem várias alternativas ao synchronized, cada uma resolvendo os mesmos problemas com menos limitações ou melhor desempenho.

Lock do java.util.concurrent

A interface Lock com as implementações ReentrantLock e ReadWriteLock fornece timeouts, espera interrompível e múltiplas filas Condition. É mais flexível que synchronized mas requer libertação explícita no finally, o que aumenta o risco de erro se o unlock for esquecido.

Classes atómicas

AtomicInteger, AtomicLong, AtomicReference e outras classes usam algoritmos Lock-Free baseados em CAS (Compare-And-Swap). São significativamente mais rápidas que synchronized em cenários de contenção moderada porque não bloqueiam threads mas realizam tentativas otimistas sem necessitar de comutação de contexto do kernel do SO.

ThreadLocal e segurança entre threads

ThreadLocal fornece uma abordagem alternativa: cada variável ThreadLocal está isolada dentro de uma única thread e não requer sincronização para leitura e escrita. Isto elimina completamente a necessidade de synchronized para dados que não devem ser partilhados entre threads. ThreadLocal é ativamente usado em frameworks (Spring, Hibernate) para armazenar contexto de transações e sessões.

Corrotinas e abordagem compose

Em projetos Kotlin para Android, uma alternativa ao synchronized é Mutex do kotlinx.coroutines. Não bloqueia a thread do sistema operativo mas suspende a corrotina até o bloqueio ser libertado — isto permite uma utilização eficiente das threads do pool e evita ANR durante longas esperas pela libertação do recurso.

kotlin
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock

val mutex = Mutex()
var counter = 0

suspend fun safeIncrement() {
    mutex.withLock {
        counter++
    }
}

Desempenho do synchronized

O desempenho do synchronized mudou significativamente nas versões recentes do Java. Antes era considerado um mecanismo “pesado”, mas as JVMs modernas eliminaram a maior parte da sobrecarga através de otimizações avançadas do compilador JIT. Vejamos em detalhe como a máquina virtual acelera o código sincronizado em tempo de execução.

Biased Locking e Lock Coarsening

O compilador JIT da JVM aplica várias otimizações: biased locking elimina a sincronização se o bloqueio for sempre adquirido pela mesma thread; lock coarsening funde blocos synchronized adjacentes num só; lock elimination remove a sincronização se o objeto for apenas acessível por uma thread. Estas otimizações tornam o synchronized praticamente gratuito com baixa contenção.

Medição de contenção e escolha de otimização

A JVM determina o nível de contenção para cada objeto: quando não há contenção, o biased locking é ativado; quando uma segunda thread aparece, o bloqueio transita para modo leve com espera ativa (spin); e apenas durante espera prolongada escala para pesado com um mutex do sistema. Esta escalada ocorre automaticamente, e o programador não precisa de escolher manualmente uma estratégia.

Comparação com Lock e classes atómicas

Em benchmarks modernos (Java 17+), synchronized mostra desempenho comparável ao ReentrantLock com contenção baixa e moderada. Com alta contenção, o Lock pode ter vantagem graças a uma fila de espera mais eficiente com suporte de timeouts e interrupção. Para sistemas de alta carga onde a contenção é constante, o ReentrantLock com modo fair oferece um comportamento mais previsível.

As classes atómicas (AtomicInteger, AtomicReference) continuam a ser as mais rápidas para contadores e flags simples graças à implementação Lock-Free baseada em CAS. Não bloqueiam threads de todo — em caso de conflito, a operação simplesmente repete-se num ciclo. Isto proporciona um ganho de desempenho de 3 a 5 vezes em comparação com synchronized em operações de incremento de contador com 4-8 threads.

Perguntas frequentes

Qual é a diferença entre synchronized e volatile?

Synchronized fornece tanto exclusão mútua como visibilidade. Volatile apenas garante visibilidade de alterações — escrever numa variável volatile é visível para todas as threads mas não impede a modificação simultânea, ou seja, não protege contra condições de corrida.

Pode o synchronized causar deadlock?

Sim, é possível um deadlock com sincronização aninhada usando diferentes ordens de monitores. Por exemplo, uma thread chama synchronized(a) { synchronized(b) }, enquanto outra chama synchronized(b) { synchronized(a) }. Evite blocos synchronized aninhados ou fixe uma ordem de monitores consistente.

O que é um monitor em Java?

Um monitor é um mecanismo de sincronização associado a cada objeto Java. Garante que apenas uma thread executa código synchronized nesse objeto. O monitor inclui um bloqueio, uma fila de espera e um conjunto de threads à espera de notificação através de wait/notify.

O Lock é mais rápido que o synchronized?

Nas versões modernas do Java (17+), synchronized não fica atrás do Lock em desempenho graças às otimizações JIT (biased locking, lock coarsening). O Lock é preferido não pela velocidade mas pelas capacidades adicionais: timeouts, espera interrompível e múltiplas filas Condition.

Como funciona o synchronized com métodos estáticos?

Um método estático synchronized usa o monitor do objeto Class da classe dada, não da instância. Isto significa que a sincronização se aplica a todas as instâncias da classe. Métodos synchronized não estáticos e estáticos usam monitores diferentes e não se bloqueiam mutuamente.

Resumo

  • Synchronized — mecanismo de sincronização integrado do Java baseado em monitores.
  • Monitor de objeto — estrutura interna da JVM que garante exclusão mútua.
  • Métodos wait/notify são usados apenas dentro de blocos ou métodos synchronized.
  • Bloco synchronized é preferível a um método pela granularidade de sincronização mais fina.
  • Happens-before garante visibilidade de alterações entre threads ao sincronizar no mesmo objeto.
  • Deadlock — o principal risco com sincronização aninhada usando diferente ordem de monitores.
  • Alternativas — Lock, classes atómicas e corrotinas com suspending Mutex.

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