O ReentrantLock em Java é uma alternativa ao bloqueio intrínseco synchronized, oferecendo maior flexibilidade no controle de concorrência. Ambos os mecanismos suportam reentrância, mas diferem em funcionalidades como interrompibilidade, timeout e condições de sincronização.
Semelhança: Suporte à Reentrância
A reentrância permite que a mesma thread adquira um bloqueio múltiplas vezes sem causar deadlock. No caso de bloqueios não reentrantes, a thread seria impedida na segunda tentativa de aquisição.
import java.util.concurrent.locks.ReentrantLock;
public class ExemploReentrant {
private static final ReentrantLock bloqueio = new ReentrantLock();
public static void main(String[] args) {
primeiroMetodo();
}
public static void primeiroMetodo() {
bloqueio.lock();
try {
System.out.println("Executando primeiroMetodo");
segundoMetodo();
} finally {
bloqueio.unlock();
}
}
public static void segundoMetodo() {
bloqueio.lock();
try {
System.out.println("Executando segundoMetodo");
terceiroMetodo();
} finally {
bloqueio.unlock();
}
}
public static void terceiroMetodo() {
bloqueio.lock();
try {
System.out.println("Executando terceiroMetodo");
} finally {
bloqueio.unlock();
}
}
}
Diferenças Principais
1. Bloqueio Interrumpível
Diferente do synchronized, o ReentrantLock permite que uma thread interrompa o processo de aquisição do bloqueio, evitando espera infinita.
import java.util.concurrent.locks.ReentrantLock;
public class ExemploInterrumpivel {
private static final ReentrantLock bloqueio = new ReentrantLock();
public static void main(String[] args) {
Thread threadSecundaria = new Thread(() -> {
System.out.println("Thread secundária iniciando...");
try {
bloqueio.lockInterruptibly();
System.out.println("Bloqueio adquirido pela thread secundária");
} catch (InterruptedException e) {
System.out.println("Aquisição do bloqueio interrompida");
return;
} finally {
bloqueio.unlock();
}
});
bloqueio.lock();
System.out.println("Thread principal adquiriu o bloqueio");
threadSecundaria.start();
try {
Thread.sleep(1000);
threadSecundaria.interrupt();
System.out.println("Interrupção enviada à thread secundária");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
bloqueio.unlock();
}
}
}
2. Timeout na Aquisição do Bloqueio
O ReentrantLock suporta a definição de um tempo limite para tentar adquirir o bloqueio, usando métodos como tryLock(long tempo, TimeUnit unidade). Isso evita que fiquem blqoueadas indefinidamente.
3. Bloqueio Justo (Fairness)
Pode ser configurado para garantir ordem de aquisição justa entre threads, mas isso pode reduzir a performence em ambientes de alta concorrência. Alternativas como tryLock() com timeout são frequentemente preferidas.
4. Variáveis de Condição Múltiplas
Suporta múltiplas condições de sincronização, permitindo criar diferentes zonas de espera para threads, o que é útil em cenários complexos como produtor-consumidor.