Los locks explícitos permiten más control que synchronized: timeouts, comprobación no bloqueante y varias condiciones por lock. Exigen disciplina para evitar fugas y deadlocks.
tryLock()), desbloqueo en orden justo opcional y múltiples condiciones.lock() con unlock() en un bloque finally.tryLock(timeout, unit) permite abortar si no se adquiere el lock.newCondition() crea colas de espera independientes, separando señales.new ReentrantLock(true) intenta servir en orden FIFO; reduce throughput (cantidad de trabajo completado por unidad de tiempo).import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class BufferLock {
private final ReentrantLock lock = new ReentrantLock(true); // justo
private final Condition noVacio = lock.newCondition();
private final Condition noLleno = lock.newCondition();
private int valor;
private boolean disponible = false;
public void producir(int nuevo) throws InterruptedException {
lock.lock();
try {
while (disponible) {
noLleno.await();
}
valor = nuevo;
disponible = true;
noVacio.signal();
} finally {
lock.unlock();
}
}
public int consumir() throws InterruptedException {
lock.lock();
try {
while (!disponible) {
if (!noVacio.await(1, TimeUnit.SECONDS)) {
throw new IllegalStateException("Timeout esperando datos");
}
}
disponible = false;
noLleno.signal();
return valor;
} finally {
lock.unlock();
}
}
public static void main(String[] args) throws Exception {
BufferLock buffer = new BufferLock();
Runnable productor = () -> {
for (int i = 1; i <= 5; i++) {
try {
buffer.producir(i);
System.out.println("Producido " + i);
TimeUnit.MILLISECONDS.sleep(150);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
}
};
Runnable consumidor = () -> {
for (int i = 1; i <= 5; i++) {
try {
int dato = buffer.consumir();
System.out.println("Consumido " + dato);
TimeUnit.MILLISECONDS.sleep(250);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
}
};
Thread tProd = new Thread(productor, "prod");
Thread tCons = new Thread(consumidor, "cons");
tProd.start();
tCons.start();
tProd.join();
tCons.join();
System.out.println("Terminado");
}
}
Partes clave:
new ReentrantLock(true) reduce hambre repartiendo el acceso en orden FIFO.noVacio y noLleno representan colas independientes para esperar datos o espacio.while protegen de señales espurias y revalidan la condición antes de continuar.await(1, TimeUnit.SECONDS) evita bloqueos indefinidos y lanza excepción si no hay progreso.noVacio.signal() y el consumidor noLleno.signal() para despertar solo al grupo relevante.readLock() concurrente: permite múltiples lectores simultáneos.writeLock() exclusivo: un solo escritor, bloquea lectores y otros escritores.tryOptimisticRead() permite leer sin bloquear y validar si hubo escrituras.readLock() para lecturas coordinadas.writeLock() para mutaciones.unlock() lleva a deadlocks.