Como corrigir ReentrantLock causando IllegalMonitorStateException?
O erro IllegalMonitorStateException
ao usar ReentrantLock
ocorre quando tentamos chamar métodos de sincronização como await()
, signal()
ou signalAll()
sem adquirir o bloqueio corretamente. Diferente do synchronized
, ReentrantLock
exige que o lock seja explicitamente adquirido e liberado.
Causas comuns do erro com ReentrantLock
Tentar chamar await()
ou signal()
sem bloquear o lock.
Liberar um lock que não foi adquirido pela mesma thread.
Não utilizar lock()
antes de manipular condições.
Exemplo de erro e solução
Código que gera o erro:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Exemplo {
private static final Lock lock = new ReentrantLock();
private static final Condition condition = lock.newCondition();
public static void main(String[] args) {
condition.signal(); // Lançará IllegalMonitorStateException pois não há lock adquirido
}
}
O erro ocorre porque signal()
foi chamado sem antes adquirir o bloqueio.
Correção:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Exemplo {
private static final Lock lock = new ReentrantLock();
private static final Condition condition = lock.newCondition();
public static void main(String[] args) {
lock.lock(); // Adquirindo o lock antes de chamar signal()
try {
condition.signal();
} finally {
lock.unlock(); // Sempre liberar o lock
}
}
}
A solução correta é garantir que o lock foi adquirido antes de chamar métodos de condição.
Como evitar IllegalMonitorStateException ao usar ReentrantLock?
Sempre utilize lock.lock()
antes de chamar await()
, signal()
ou signalAll()
.
Garanta que o lock seja liberado corretamente utilizando try-finally
.
Evite liberar um lock que não foi adquirido pela mesma thread.
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ExemploSeguro {
private static final Lock lock = new ReentrantLock();
private static final Condition condition = lock.newCondition();
public static void main(String[] args) {
new Thread(() -> {
lock.lock();
try {
System.out.println("Thread aguardando...");
condition.await();
System.out.println("Thread despertada!");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
}).start();
new Thread(() -> {
lock.lock();
try {
System.out.println("Sinalizando thread...");
condition.signal();
} finally {
lock.unlock();
}
}).start();
}
}
Conclusão
O erro IllegalMonitorStateException
ao usar ReentrantLock
pode ser evitado garantindo que o lock seja sempre adquirido antes de chamar await()
, signal()
ou signalAll()
. O uso correto de try-finally
também é essencial para evitar deadlocks e garantir que o bloqueio seja liberado adequadamente.
Boas práticas para evitar IllegalMonitorStateException ao usar ReentrantLock
ReentrantLock é uma alternativa poderosa ao synchronized para manipulação de concorrência em Java, mas seu uso incorreto pode levar a erros como IllegalMonitorStateException. Esse erro ocorre quando métodos como await() ou signal() são chamados sem que o lock tenha sido adquirido corretamente. Para evitar problemas, sempre garanta que a thread tenha o lock antes de manipular condições.
Algumas aplicações:
- Garantir controle seguro sobre concorrência em Java
- Evitar erros ao manipular múltiplas threads
- Melhorar a sincronização de processos paralelos
- Controlar corretamente sinais e bloqueios entre threads
Dicas para quem está começando
- Sempre adquira o lock antes de chamar await() ou signal()
- Use try-finally para liberar o lock corretamente
- Evite liberar locks em threads diferentes da que os adquiriu
- Utilize Condition ao invés de wait() e notify() quando estiver usando ReentrantLock
Contribuições de Rodrigo Farias