Bloqueo y Condición
En Java, la sincronización estándar se basa en la palabra clave synchronized y en los objetos monitor incorporados. Sin embargo, en algunos casos, synchronized puede no ser suficiente, especialmente cuando se requiere mayor flexibilidad en la gestión de hilos.
Descripción general
Las interfaces Lock y Condition, introducidas en el paquete java.util.concurrent.locks, proporcionan capacidades avanzadas de gestión de hilos.
En esta imagen, se puede observar que el primer hilo adquiere el bloqueo utilizando el método lock(), y en ese momento, otro hilo no puede adquirir el mismo bloqueo. Tan pronto como se ejecuta todo el código dentro del bloqueo, se llamará al método unlock() y se liberará el bloqueo. Solo después de eso, el segundo hilo podrá adquirir el bloqueo.
Diferencia
La diferencia entre estas dos interfaces es que las implementaciones de Lock son una alternativa de alto nivel al bloque synchronized, y las implementaciones de la interfaz Condition son una alternativa a los métodos notify()/wait(). Ambas interfaces forman parte del paquete java.util.concurrent.locks.
Ejemplos de la vida real
Imagina que estás gestionando una cola para registro en un evento. Para evitar el desbordamiento y asegurar una asignación adecuada de asientos, es necesario utilizar mecanismos de bloqueo y condiciones para mantener el flujo de nuevas inscripciones en espera hasta que haya un asiento libre disponible.
Clase ReentrantLock
La clase ReentrantLock del paquete java.util.concurrent.locks es una implementación de la interfaz Lock. Proporciona funcionalidad para gestionar bloqueos de manera explícita.
Métodos principales de ReentrantLock:
lock(): Captura un bloqueo;unlock(): Libera el bloqueo;tryLock(): Intenta capturar el bloqueo y devuelve true si la captura es exitosa;tryLock(long timeout, TimeUnit unit): Intenta capturar el bloqueo durante el tiempo especificado;newCondition(): Crea una condición para elLockactual.
Main.java
12345678910111213141516171819202122232425262728293031package com.example; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Main { // Creating a ReentrantLock object private final Lock lock = new ReentrantLock(); private int count = 0; // Method to increment the `count` variable public void increment() { lock.lock(); // Acquiring the `lock` try { count++; System.out.println("Count incremented to: " + count); } finally { lock.unlock(); // Releasing the `lock` } } public static void main(String[] args) { Main example = new Main(); // Runnable task to call the increment method Runnable task = example::increment; // Starting multiple threads to execute the task for (int i = 0; i < 5; i++) { new Thread(task).start(); } } }
Como puedes observar, en el método increment() utilizamos el bloqueo con Lock. Cuando un hilo entra en el método, adquiere el bloqueo con lock.lock(), luego ejecuta el código y, en el bloque finally, liberamos el bloqueo con lock.unlock() para que otros hilos puedan ingresar.
Liberamos el bloqueo en el bloque finally por una razón: este bloque casi siempre se ejecuta, incluso en presencia de excepciones, excepto cuando se termina el programa.
Interfaz Condition
Solo podemos crear un Condition asociado a una implementación específica de Lock. Por esta razón, los métodos de Condition solo afectarán el bloqueo de esa implementación particular de Lock.
Main.java
12private final Lock lock = new ReentrantLock(); private final Condition condition = lock.newCondition();
Métodos principales de Condition:
await(): Espera una señal de otro hilo;signal(): Desbloquea un hilo en espera de una condición;signalAll(): Desbloquea todos los hilos en espera de la condición.
Main.java
12345678910111213141516private final ReentrantLock lock = new ReentrantLock(); private final Condition condition = lock.newCondition(); private boolean ready = false; public void waitForCondition() throws InterruptedException { lock.lock(); // Acquire the `lock` try { while (!ready) { // Check if the condition is not met System.out.println("Waiting..."); // Print a waiting message condition.await(); // Wait for the condition to be signaled } System.out.println("Condition met!"); // Print a message when the condition is met } finally { lock.unlock(); // Release the `lock` } }
El método waitForCondition() bloquea el hilo hasta que la variable ready se vuelve true, lo que indica que se ha cumplido la condición. Cuando se cumple la condición, el hilo continúa ejecutándose y muestra el mensaje “¡Condición cumplida!”
Cuando se llama al método await(), el hilo se pausa y también libera el bloqueo que ha capturado. Cuando el hilo se despierta, debe capturar el bloqueo nuevamente y solo entonces comenzará a ejecutarse.
Ejemplo de código
Ahora veamos un ejemplo de cómo usar ReentrantLock y Condition para gestionar el registro en un evento:
Un breve fragmento del video
Bloqueo con ReentrantLock: El método register() adquiere el bloqueo con lock.lock() para evitar que varios hilos ejecuten el código al mismo tiempo.
Condición con Condition: Si no hay espacios disponibles, el hilo invoca spaceAvailable.await() para esperar hasta que haya espacio disponible.
Liberar bloqueo: Cuando un hilo ha liberado espacio usando el método cancel(), invoca spaceAvailable.signalAll() para notificar a todos los hilos en espera.
Manejo de excepciones: El uso de bloques try-finally garantiza que el bloqueo se libere incluso si ocurre una excepción.
El uso de Lock y Condition en Java permite un control más flexible sobre los hilos y la sincronización que el mecanismo tradicional de synchronized. Esto resulta especialmente útil en escenarios complejos donde se requiere un control más preciso sobre los hilos y las condiciones de espera.
¡Gracias por tus comentarios!
Pregunte a AI
Pregunte a AI
Pregunte lo que quiera o pruebe una de las preguntas sugeridas para comenzar nuestra charla
Can you explain the main differences between Lock and Condition in Java?
How does ReentrantLock improve thread management compared to synchronized blocks?
Can you provide a simple example of using Condition with ReentrantLock?
Awesome!
Completion rate improved to 3.33
Bloqueo y Condición
Desliza para mostrar el menú
En Java, la sincronización estándar se basa en la palabra clave synchronized y en los objetos monitor incorporados. Sin embargo, en algunos casos, synchronized puede no ser suficiente, especialmente cuando se requiere mayor flexibilidad en la gestión de hilos.
Descripción general
Las interfaces Lock y Condition, introducidas en el paquete java.util.concurrent.locks, proporcionan capacidades avanzadas de gestión de hilos.
En esta imagen, se puede observar que el primer hilo adquiere el bloqueo utilizando el método lock(), y en ese momento, otro hilo no puede adquirir el mismo bloqueo. Tan pronto como se ejecuta todo el código dentro del bloqueo, se llamará al método unlock() y se liberará el bloqueo. Solo después de eso, el segundo hilo podrá adquirir el bloqueo.
Diferencia
La diferencia entre estas dos interfaces es que las implementaciones de Lock son una alternativa de alto nivel al bloque synchronized, y las implementaciones de la interfaz Condition son una alternativa a los métodos notify()/wait(). Ambas interfaces forman parte del paquete java.util.concurrent.locks.
Ejemplos de la vida real
Imagina que estás gestionando una cola para registro en un evento. Para evitar el desbordamiento y asegurar una asignación adecuada de asientos, es necesario utilizar mecanismos de bloqueo y condiciones para mantener el flujo de nuevas inscripciones en espera hasta que haya un asiento libre disponible.
Clase ReentrantLock
La clase ReentrantLock del paquete java.util.concurrent.locks es una implementación de la interfaz Lock. Proporciona funcionalidad para gestionar bloqueos de manera explícita.
Métodos principales de ReentrantLock:
lock(): Captura un bloqueo;unlock(): Libera el bloqueo;tryLock(): Intenta capturar el bloqueo y devuelve true si la captura es exitosa;tryLock(long timeout, TimeUnit unit): Intenta capturar el bloqueo durante el tiempo especificado;newCondition(): Crea una condición para elLockactual.
Main.java
12345678910111213141516171819202122232425262728293031package com.example; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Main { // Creating a ReentrantLock object private final Lock lock = new ReentrantLock(); private int count = 0; // Method to increment the `count` variable public void increment() { lock.lock(); // Acquiring the `lock` try { count++; System.out.println("Count incremented to: " + count); } finally { lock.unlock(); // Releasing the `lock` } } public static void main(String[] args) { Main example = new Main(); // Runnable task to call the increment method Runnable task = example::increment; // Starting multiple threads to execute the task for (int i = 0; i < 5; i++) { new Thread(task).start(); } } }
Como puedes observar, en el método increment() utilizamos el bloqueo con Lock. Cuando un hilo entra en el método, adquiere el bloqueo con lock.lock(), luego ejecuta el código y, en el bloque finally, liberamos el bloqueo con lock.unlock() para que otros hilos puedan ingresar.
Liberamos el bloqueo en el bloque finally por una razón: este bloque casi siempre se ejecuta, incluso en presencia de excepciones, excepto cuando se termina el programa.
Interfaz Condition
Solo podemos crear un Condition asociado a una implementación específica de Lock. Por esta razón, los métodos de Condition solo afectarán el bloqueo de esa implementación particular de Lock.
Main.java
12private final Lock lock = new ReentrantLock(); private final Condition condition = lock.newCondition();
Métodos principales de Condition:
await(): Espera una señal de otro hilo;signal(): Desbloquea un hilo en espera de una condición;signalAll(): Desbloquea todos los hilos en espera de la condición.
Main.java
12345678910111213141516private final ReentrantLock lock = new ReentrantLock(); private final Condition condition = lock.newCondition(); private boolean ready = false; public void waitForCondition() throws InterruptedException { lock.lock(); // Acquire the `lock` try { while (!ready) { // Check if the condition is not met System.out.println("Waiting..."); // Print a waiting message condition.await(); // Wait for the condition to be signaled } System.out.println("Condition met!"); // Print a message when the condition is met } finally { lock.unlock(); // Release the `lock` } }
El método waitForCondition() bloquea el hilo hasta que la variable ready se vuelve true, lo que indica que se ha cumplido la condición. Cuando se cumple la condición, el hilo continúa ejecutándose y muestra el mensaje “¡Condición cumplida!”
Cuando se llama al método await(), el hilo se pausa y también libera el bloqueo que ha capturado. Cuando el hilo se despierta, debe capturar el bloqueo nuevamente y solo entonces comenzará a ejecutarse.
Ejemplo de código
Ahora veamos un ejemplo de cómo usar ReentrantLock y Condition para gestionar el registro en un evento:
Un breve fragmento del video
Bloqueo con ReentrantLock: El método register() adquiere el bloqueo con lock.lock() para evitar que varios hilos ejecuten el código al mismo tiempo.
Condición con Condition: Si no hay espacios disponibles, el hilo invoca spaceAvailable.await() para esperar hasta que haya espacio disponible.
Liberar bloqueo: Cuando un hilo ha liberado espacio usando el método cancel(), invoca spaceAvailable.signalAll() para notificar a todos los hilos en espera.
Manejo de excepciones: El uso de bloques try-finally garantiza que el bloqueo se libere incluso si ocurre una excepción.
El uso de Lock y Condition en Java permite un control más flexible sobre los hilos y la sincronización que el mecanismo tradicional de synchronized. Esto resulta especialmente útil en escenarios complejos donde se requiere un control más preciso sobre los hilos y las condiciones de espera.
¡Gracias por tus comentarios!