Sobes.tech
Middle

Quais são os métodos básicos de sincronização em Java?

sobes.tech IA

Resposta da IA

Formas básicas de sincronização em Java:

  1. Palavra-chave synchronized:

    • Usada para sincronizar métodos ou blocos de código.
    • Um método marcado como synchronized bloqueia o acesso à instância do objeto para outros métodos sincronizados dessa mesma instância.
    • synchronized (this): sincronização na instância atual do objeto.
    • synchronized (ClassName.class): sincronização ao nível da classe, bloqueia o acesso para todos os threads que trabalham com essa classe.
    • synchronized (object): sincronização em um monitor de objeto específico.
    class Counter {
        private int count = 0;
    
        // Método sincronizado
        public synchronized void increment() {
            count++;
        }
    
        public void decrement() {
            // Bloco sincronizado
            synchronized (this) {
                count--;
            }
        }
    }
    
  2. Palavra-chave volatile:

    • Garante a visibilidade das mudanças na variável entre threads.
    • Não garante a atomicidade de operações complexas (por exemplo, i++), apenas leitura e escrita.
    • Aplica-se a campos da classe.
    class SharedData {
        volatile boolean flag = false;
    
        public void setFlag(boolean value) {
            flag = value; // A mudança na flag torna-se visível para outros threads
        }
    
        public boolean getFlag() {
            return flag;
        }
    }
    
  3. Bloqueios explícitos (Locks explícitos):

    • Oferecem um controle mais flexível em comparação com synchronized.
    • Interface Lock e suas implementações, como ReentrantLock.
    • Permitem escolher entre modo justo e injusto de bloqueio, obter o estado do bloqueio, interromper o thread durante a espera pelo bloqueio.
    • Requerem chamadas explícitas a lock() e unlock(). É importante usar um bloco finally para chamar unlock().
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    class Resource {
        private final Lock lock = new ReentrantLock();
    
        public void performAction() {
            lock.lock(); // Aquisição do bloqueio
            try {
                // Seção crítica
                System.out.println(Thread.currentThread().getName() + " adquiriu o lock");
                Thread.sleep(100); // Simulação de trabalho
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } finally {
                lock.unlock(); // Liberação do bloqueio
                System.out.println(Thread.currentThread().getName() + " liberou o lock");
            }
        }
    }
    
  4. wait(), notify(), notifyAll():

    • Métodos da classe Object, usados para interação entre threads que aguardam a liberação do monitor.
    • wait(): libera o monitor e coloca o thread em estado de espera até que notify() ou notifyAll() seja chamado.
    • notify(): acorda um thread que espera nesse monitor.
    • notifyAll(): acorda todos os threads que aguardam nesse monitor.
    • Devem ser chamados dentro de um bloco ou método sincronizado no mesmo objeto monitor.
    class ProducerConsumer {
        private final Object monitor = new Object();
        private boolean isProduced = false;
    
        public void produce() throws InterruptedException {
            synchronized (monitor) {
                while (isProduced) {
                    monitor.wait(); // Espera até que o consumidor consuma
                }
                // Produz
                System.out.println("Produzido");
                isProduced = true;
                monitor.notifyAll(); // Notifica os consumidores
            }
        }
    
        public void consume() throws InterruptedException {
            synchronized (monitor) {
                while (!isProduced) {
                    monitor.wait(); // Espera até que o produtor produza
                }
                // Consome
                System.out.println("Consumido");
                isProduced = false;
                monitor.notifyAll(); // Notifica os produtores
            }
        }
    }
    

Estes mecanismos são a base para criar código multithread seguro em Java. A escolha de um método específico depende da natureza da tarefa e do nível de controle necessário sobre a sincronização.