Sobes.tech
Middle

O que é o bloco synchronized em Java e que problemas podem surgir ao usá-lo?

sobes.tech IA

Resposta da IA

O bloco synchronized em Java é usado para garantir a sincronização do acesso a um recurso compartilhado entre vários threads. Isso garante que apenas um thread possa executar o código dentro do bloco em qualquer momento.

Estrutura do bloco synchronized:

synchronized (objeto_monitor) {
    // Seção crítica do código
    // Operações com o recurso compartilhado
}
  • objeto_monitor: Qualquer objeto que atua como monitor (lock). Os threads que tentarem entrar em um bloco synchronized com o mesmo objeto monitor serão bloqueados até que o thread atual saia do bloco.

Problemas que podem surgir ao usar synchronized:

  • Interbloqueio (Deadlock): Dois ou mais threads esperam liberar recursos que estão sendo utilizados por outros threads, levando o programa a ficar travado. Isso pode acontecer se os threads tentarem obter bloqueios em ordens diferentes.
  • Redução de desempenho: A sincronização bloqueia a execução de outros threads que aguardam acesso ao monitor, o que pode desacelerar aplicações paralelas.
  • Inaplicável em certos cenários: O bloco synchronized fornece acesso exclusivo. Em alguns casos, pode ser necessário um controle de acesso mais flexível (por exemplo, leitura por múltiplos threads simultaneamente, escrita por um único thread).
  • Complexidade na depuração: Problemas de sincronização podem ser pouco evidentes e difíceis de reproduzir e depurar.

Exemplo de interbloqueio:

class Resource {
    private Object lock1 = new Object();
    private Object lock2 = new Object();

    public void method1(Resource other) {
        synchronized (lock1) {
            try {
                Thread.sleep(100); // Simulação de trabalho
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " adquiriu lock1");
            synchronized (other.lock2) {
                System.out.println(Thread.currentThread().getName() + " adquiriu o lock2 do outro recurso");
            }
        }
    }

    public void method2(Resource other) {
        synchronized (lock2) {
             try {
                Thread.sleep(100); // Simulação de trabalho
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " adquiriu lock2");
            synchronized (other.lock1) {
                System.out.println(Thread.currentThread().getName() + " adquiriu o lock1 do outro recurso");
            }
        }
    }
}

public class DeadlockExample {
    public static void main(String[] args) {
        Resource r1 = new Resource();
        Resource r2 = new Resource();

        new Thread(() -> r1.method1(r2), "Thread-1").start();
        new Thread(() -> r2.method2(r1), "Thread-2").start();
    }
}

Neste exemplo, Thread-1 tenta obter lock1 em r1 e depois lock2 em r2. Thread-2 tenta obter lock2 em r2 e depois lock1 em r1. Se ambos obtiverem as primeiras bloqueios simultaneamente, ocorrerá um interbloqueio.