Objetos De Sincronización Lock Y Condition
Objetos de Sincronización: Lock y Condition
En el desarrollo de software concurrente, es fundamental garantizar que los hilos accedan de manera segura y eficiente a los recursos compartidos. Java proporciona herramientas avanzadas para manejar la sincronización entre hilos, siendo los objetos Lock y Condition parte del paquete java.util.concurrent.locks. En este documento, aprenderás cómo funcionan, cómo usarlos y las mejores prácticas para aplicarlos.
1. Introducción a Lock
El objeto Lock es una interfaz que proporciona un mecanismo de sincronización más flexible que el uso de los bloques sincronizados (synchronized). Mientras que synchronized utiliza un enfoque implícito, Lock permite un control explícito sobre el bloqueo.
Principales métodos de Lock
lock(): Adquiere el bloqueo. Si el bloqueo ya está ocupado, el hilo se bloquea hasta que esté disponible.unlock(): Libera el bloqueo. Debe ser llamado siempre en un bloquefinallypara evitar que el bloqueo quede permanentemente ocupado.tryLock(): Intenta adquirir el bloqueo sin esperar indefinidamente. Devuelvetruesi tiene éxito.tryLock(long time, TimeUnit unit): Intenta adquirir el bloqueo dentro de un tiempo determinado.lockInterruptibly(): Adquiere el bloqueo pero permite que el hilo sea interrumpido mientras espera.
Ejemplo de uso de Lock
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class EjemploLock {
private final Lock lock = new ReentrantLock();
private int recursoCompartido = 0;
public void incrementar() {
lock.lock();
try {
recursoCompartido++;
System.out.println("Recurso incrementado: " + recursoCompartido);
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
EjemploLock ejemplo = new EjemploLock();
Runnable tarea = ejemplo::incrementar;
Thread hilo1 = new Thread(tarea);
Thread hilo2 = new Thread(tarea);
hilo1.start();
hilo2.start();
}
}
Ventajas de Lock sobre synchronized
- Mayor flexibilidad para adquirir y liberar bloqueos.
- Capacidad de interrumpir hilos mientras esperan un bloqueo.
- Intento de adquirir el bloqueo sin bloquear indefinidamente.
2. Introducción a Condition
La interfaz Condition funciona junto con Lock para manejar la comunicación entre hilos. Es una alternativa más flexible a los métodos wait(), notify() y notifyAll() de los objetos sincronizados.
Principales métodos de Condition
await(): Hace que el hilo actual espere hasta que seá notificado o interrumpido.signal(): Despierta un hilo que esté esperando en la condición.signalAll(): Despierta a todos los hilos que estén esperando en la condición.
Ejemplo de uso de Condition
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class EjemploCondition {
private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
private boolean listo = false;
public void esperar() {
lock.lock();
try {
while (!listo) {
System.out.println("Esperando...");
condition.await();
}
System.out.println("Condición cumplida, continuando...");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
}
public void señal() {
lock.lock();
try {
listo = true;
condition.signal();
System.out.println("Condición señalada.");
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
EjemploCondition ejemplo = new EjemploCondition();
Thread hiloEsperar = new Thread(ejemplo::esperar);
Thread hiloSeñalar = new Thread(() -> {
try {
Thread.sleep(2000); // Simula trabajo
ejemplo.señal();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
hiloEsperar.start();
hiloSeñalar.start();
}
}
Ventajas de Condition sobre wait y notify
- Mayor control sobre las notificaciones de hilos.
- Uso de múltiples condiciones asociadas a un solo bloqueo.
- Mejora la legibilidad y organización del código.
3. Buenas Prácticas
- Siempre usar
finallypara liberar bloqueos: Esto evita que los bloqueos queden ocupados en caso de excepciones. - Evitar bloqueos innecesarios: Usa
tryLockcuando sea posible para evitar deadlocks. - Documentar condiciones y bloqueos: Facilita el mantenimiento del código.
- Usar variables volátiles cuando sea necesario: Complementa el uso de
Lockpara garantizar la visibilidad de los cambios entre hilos. - Minimizar la sección crítica: Reduce el tiempo en que los recursos compartidos están bloqueados.