Atualizado: 07/09/2025

Este conteúdo é original e não foi gerado por inteligência artificial.

A Interface Condition e Bloqueios ReentrantLock em Java

A interface Condition, parte do pacote java.util.concurrent.locks, eleva o nível da coordenação entre threads, oferecendo um mecanismo mais flexível do que os métodos wait() e notify() intrínsecos a cada objeto. Uma Condition está sempre associada a uma instância de Lock, geralmente ReentrantLock.

Enquanto wait() e notify() operam em um único conjunto de espera por objeto, a principal vantagem da Condition é que um único Lock pode ter múltiplos objetos Condition. Isso permite criar diferentes grupos de espera para threads, separando-as por diferentes "razões" para esperar, tudo sob a proteção do mesmo bloqueio.

Por exemplo, em um buffer limitado, é possível ter:

  • Uma condição notFull para os produtores esperarem quando o buffer estiver cheio.
  • Uma condição notEmpty para os consumidores esperarem quando o buffer estiver vazio.

Isso permite sinalizações mais eficientes: quando um produtor adiciona um item, ele pode sinalizar (signal()) especificamente a condição notEmpty, acordando apenas consumidores, em vez de acordar outros produtores desnecessariamente com notifyAll().

Os métodos da Condition são análogos diretos aos de Object:

  • await(): Faz com que a thread atual libere o Lock associado e entre em estado de espera nesta condição específica.
  • signal(): Acorda uma thread que está esperando nesta condição.
  • signalAll(): Acorda todas as threads que estão esperando nesta condição.

Esses métodos devem ser invocados apenas quando a thread atual detém o Lock associado à Condition.

Exemplo: Produtor-Consumidor com ReentrantLock e Condition

Vamos refatorar o problema do Produtor-Consumidor para usar ReentrantLock e uma única Condition. Este exemplo demonstra a substituição direta de synchronized e wait/notify.

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

class Store {
    private int productCount = 0;
    private final int MAX_PRODUCTS = 3;
    private ReentrantLock locker;
    private Condition condition;

    Store() {
        locker = new ReentrantLock();
        condition = locker.newCondition(); // Obtém UMA condição associada ao bloqueio
    }

    public void get() {
        locker.lock();
        try {
            while (productCount < 1) {
                System.out.println("Estoque vazio, consumidor esperando...");
                condition.await();
            }
            productCount--;
            System.out.println("Consumidor comprou 1 produto.");
            System.out.println("Produtos no estoque: " + productCount);
            condition.signalAll(); // Sinaliza para as threads em espera (produtor)
        } catch (InterruptedException e) {
            System.err.println(e.getMessage());
            Thread.currentThread().interrupt();
        } finally {
            locker.unlock();
        }
    }

    public void put() {
        locker.lock();
        try {
            while (productCount >= MAX_PRODUCTS) {
                System.out.println("Estoque cheio, produtor esperando...");
                condition.await();
            }
            productCount++;
            System.out.println("Produtor adicionou 1 produto.");
            System.out.println("Produtos no estoque: " + productCount);
            condition.signalAll(); // Sinaliza para as threads em espera (consumidor)
        } catch (InterruptedException e) {
            System.err.println(e.getMessage());
            Thread.currentThread().interrupt();
        } finally {
            locker.unlock();
        }
    }
}

// Classes Producer e Consumer, e a classe Program com o método main, permanecem as mesmas.
// ...

O fluxo é idêntico ao da versão com wait/notify, mas o controle é explícito através dos objetos locker e condition. O método await() libera o bloqueio, permitindo que a outra thread o adquira. Após ser sinalizada, a thread que estava em espera deve readquirir o bloqueio antes de continuar.

O resultado da execução demonstra a coordenação:

Produtor adicionou 1 produto.
Produtos no estoque: 1
Produtor adicionou 1 produto.
Produtos no estoque: 2
Produtor adicionou 1 produto.
Produtos no estoque: 3
Estoque cheio, produtor esperando...
Consumidor comprou 1 produto.
Produtos no estoque: 2
...

Resumo

  • A interface Condition é uma alternativa flexível aos métodos wait() e notify().
  • Um objeto Condition é sempre obtido a partir de um Lock (locker.newCondition()).
  • Os métodos await(), signal() e signalAll() só podem ser chamados quando a thread detém o Lock associado.
  • A principal vantagem é a capacidade de um Lock ter múltiplos objetos Condition, permitindo a criação de diferentes conjuntos de espera e sinalizações mais eficientes.
  • O padrão de uso deve sempre seguir a estrutura lock(), try, await/signal, finally, unlock().
Política de Privacidade

Copyright © www.programicio.com Todos os direitos reservados

É proibida a reprodução do conteúdo desta página sem autorização prévia do autor.

Contato: programicio@gmail.com