BlockingQueue y Sus Implementaciones
Implementaciones básicas de BlockingQueue
No revisaremos cada implementación en detalle, ya que tomaría mucho tiempo y es poco probable que necesites todas. Hablaré sobre los conceptos generales y qué constructores tienen.
Ejemplo de la vida real
Imagina una fábrica donde un hilo, el productor, crea piezas, y otro hilo, el consumidor, las procesa. El productor coloca las piezas en una cola, mientras que el consumidor las recupera y procesa desde la cola. Si la cola se queda sin piezas, el consumidor espera a que el productor agregue más. Por el contrario, si la cola está llena, el productor espera a que el consumidor libere espacio.
Un poco más abajo implementaremos esta tarea en código.
Diferencias con otros tipos de colecciones
BlockingQueue proporciona sincronización automatizada, gestionando el acceso de los hilos a la cola sin requerir sincronización manual. También admite operaciones de bloqueo para agregar y recuperar elementos, una característica que no se encuentra en otras colecciones como ArrayList o LinkedList.
Implementaciones de BlockingQueue
ArrayBlockingQueue: Una cola limitada en tamaño que utiliza un arreglo para almacenar los elementos.
Main.java
123456789// Constructor with fixed capacity BlockingQueue<String> queue1 = new ArrayBlockingQueue<>(5); // Constructor with fixed capacity and fair access BlockingQueue<String> queue2 = new ArrayBlockingQueue<>(5, true); // Constructor with fixed capacity and initial collection of elements Collection<String> initialElements = java.util.Arrays.asList("One", "Two", "Three"); BlockingQueue<String> queue3 = new ArrayBlockingQueue<>(5, false, initialElements);
El parámetro true habilita una política de acceso justa proporcionando un orden FIFO para el acceso de los hilos.
LinkedBlockingQueueue: Una cola basada en nodos enlazados que puede ser restringida o irrestringida.
Main.java
123456789// Constructor without capacity bounds BlockingQueue<String> queue1 = new LinkedBlockingQueue<>(); // Constructor with fixed capacity BlockingQueue<String> queue2 = new LinkedBlockingQueue<>(5); // Constructor with initial collection of elements Collection<String> initialElements = java.util.Arrays.asList("One", "Two", "Three"); BlockingQueue<String> queue3 = new LinkedBlockingQueue<>(initialElements);
PriorityBlockingQueue: Una cola priorizada sin límites donde los elementos se recuperan de acuerdo con su orden natural o según lo especificado por un comparador.
Main.java
12345678910111213// Constructor without initial capacity (default is 11) BlockingQueue<Integer> queue1 = new PriorityBlockingQueue<>(); // Constructor with initial capacity BlockingQueue<Integer> queue2 = new PriorityBlockingQueue<>(5); // Constructor with initial capacity and comparator Comparator<Integer> comparator = Integer::compareTo; BlockingQueue<Integer> queue3 = new PriorityBlockingQueue<>(5, comparator); // Constructor with initial collection of elements Collection<Integer> initialElements = java.util.Arrays.asList(1, 3, 2); BlockingQueue<Integer> queue4 = new PriorityBlockingQueue<>(initialElements)
DelayQueue: Una cola retardada donde los elementos solo pueden ser recuperados después de que haya expirado su retardo.
DelayedElement.java
DelayQueueConstructors.java
123456789101112131415161718class DelayedElement implements Delayed { private final long expirationTime; // The time when the element will be available public DelayedElement(long delay, TimeUnit unit) { this.expirationTime = System.currentTimeMillis() + unit.toMillis(delay); } @Override public long getDelay(TimeUnit unit) { long delay = expirationTime - System.currentTimeMillis(); // Calculate the remaining delay return unit.convert(delay, TimeUnit.MILLISECONDS); // Convert the delay to the specified time unit } @Override public int compareTo(Delayed o) { return Long.compare(this.expirationTime, ((DelayedElement) o).expirationTime); } }
Este código demuestra el uso de la clase DelayedElement, que implementa la interfaz Delayed, y la cola retardada DelayQueue en Java. La clase DelayedElement define un método getDelay para calcular el tiempo de retardo restante y un método compareTo para comparar objetos según el tiempo de expiración del retardo.
El método main crea dos colas: queue1, una cola retardada vacía, y queue2, una cola inicializada con elementos que tienen un retardo de 5 y 1 segundo, respectivamente.
Los elementos en DelayQueueue se vuelven disponibles para su recuperación después de que haya transcurrido el tiempo de retardo especificado.
SynchronousQueueue: Una cola sin capacidad, donde cada operación de inserción debe esperar la correspondiente operación de extracción y viceversa.
Main.java
12345// Constructor without fair access BlockingQueue<String> queue1 = new SynchronousQueue<>(); // Constructor with fair access BlockingQueue<String> queue2 = new SynchronousQueue<>(true);
Los métodos principales de BlockingQueue:
Adición de elementos:
El método void put(E e) inserta un elemento en la cola, bloqueando el hilo si la cola está llena. Alternativamente, el método boolean offer(E e, long timeout, TimeUnit unit) intenta agregar un elemento a la cola, esperando el tiempo especificado si la cola está llena.
Main.java
1234567891011121314151617public class BlockingQueueExample { public static void main(String[] args) { BlockingQueue<String> queue = new ArrayBlockingQueue<>(2); try { queue.put("Element 1"); // Insert the first element, no blocking. queue.put("Element 2"); // Insert the second element, no blocking. // Try to add the third element with a 2-second timeout. // Since the queue is full, it will wait for 2 seconds. boolean success = queue.offer("Element 3", 2, TimeUnit.SECONDS); } catch (InterruptedException e) { e.printStackTrace(); } } }
Este ejemplo demuestra la inserción de dos elementos en una BlockingQueue sin bloqueo, seguida de un intento de agregar un tercer elemento con un tiempo de espera de 2 segundos utilizando el método offer(), el cual esperará si la cola está llena.
Recuperación de elementos:
El método E take() recupera y devuelve un elemento de la cola, bloqueando el hilo si la cola está vacía. Alternativamente, el método E poll(long timeout, TimeUnit unit) intenta recuperar un elemento de la cola, esperando el tiempo especificado si la cola está vacía.
Main.java
1234567891011121314151617181920212223public class BlockingQueueRetrievalExample { public static void main(String[] args) { BlockingQueue<String> queue = new ArrayBlockingQueue<>(2); try { // Adding elements to the queue queue.put("Element 1"); queue.put("Element 2"); // Retrieve and remove the first element, no blocking since the queue is not empty String item1 = queue.take(); // Returns "Element 1" // Attempt to retrieve and remove the next element with a 2-second timeout String item2 = queue.poll(2, TimeUnit.SECONDS); // Returns "Element 2" // Attempt to retrieve an element when the queue is empty, this will block for 2 seconds String item3 = queue.poll(2, TimeUnit.SECONDS); // Returns `null` after timeout } catch (InterruptedException e) { e.printStackTrace(); } } }
Este código agrega dos elementos a un BlockingQueue, recupera y elimina el primer elemento de inmediato, intenta recuperar el siguiente elemento con un tiempo de espera de 2 segundos, y finalmente intenta recuperar un elemento de una cola vacía, lo que resulta en un null después del tiempo de espera.
Comprobación y eliminación de elementos:
El método boolean remove(Object o) elimina el elemento especificado de la cola si está presente. Por otro lado, el método boolean contains(Object o) verifica si el elemento especificado está presente en la cola sin eliminarlo.
Main.java
1234567891011121314151617181920212223242526public class BlockingQueueCheckRemoveExample { public static void main(String[] args) { BlockingQueue<String> queue = new ArrayBlockingQueue<>(2); try { // Adding elements to the queue queue.put("Element 1"); queue.put("Element 2"); // Check if "Element 1" is in the queue, should return `true` boolean containsElement1 = queue.contains("Element 1"); // true // Remove "Element 1" from the queue, should return `true` boolean removedElement1 = queue.remove("Element 1"); // true // Check if "Element 1" is still in the queue, should return `false` boolean containsElement1AfterRemoval = queue.contains("Element 1"); // false // Try to remove an element that is not in the queue, should return `false` boolean removedElement3 = queue.remove("Element 3"); // false } catch (InterruptedException e) { e.printStackTrace(); } } }
Este código agrega dos elementos a un BlockingQueue, verifica la presencia de "Element 1", lo elimina, vuelve a comprobar para confirmar su eliminación y luego intenta eliminar un elemento inexistente.
Consulta el estado de la cola:
El método int size() devuelve el número de elementos que hay actualmente en la cola. Para determinar si la cola está vacía, se puede utilizar el método boolean isEmpty(), que comprueba si la cola no contiene elementos. Para colas con capacidad fija, el método int remainingCapacity() proporciona el número de espacios disponibles restantes en la cola.
Main.java
123456789101112131415161718192021222324252627282930public class BlockingQueueCapacityExample { public static void main(String[] args) { BlockingQueue<String> queue = new ArrayBlockingQueue<>(3); try { // Adding elements to the queue queue.put("Element 1"); queue.put("Element 2"); // Get the number of elements in the queue int currentSize = queue.size(); // 2 // Check if the queue is empty boolean isQueueEmpty = queue.isEmpty(); // false // Get the remaining capacity in the queue int remainingSpace = queue.remainingCapacity(); // 1 // Add another element to fill the queue queue.put("Element 3"); // Check the size and remaining capacity after adding the third element currentSize = queue.size(); // 3 remainingSpace = queue.remainingCapacity(); // 0 } catch (InterruptedException e) { e.printStackTrace(); } } }
Este código agrega elementos a un BlockingQueue, verifica el tamaño actual, comprueba si la cola está vacía y determina la capacidad restante, luego actualiza estos valores después de llenar la cola completamente.
Realización de un ejemplo de la vida real en código
😭 Limitaciones
Una limitación clave es el rendimiento: debido a las operaciones de bloqueo involucradas, el rendimiento puede verse reducido en comparación con las colecciones no sincronizadas. Además, los recursos pueden convertirse en un problema, ya que las colas grandes requieren más memoria y tiempo de CPU para gestionar los bloqueos y los procesos de sincronización.
💪 Ventajas
Por el lado positivo, el sistema es seguro en entornos multihilo, ofreciendo comunicación segura entre hilos sin requerir una gestión manual de la sincronización. También simplifica el código al evitar construcciones complejas de sincronización y bloqueo. Además, la flexibilidad de las diferentes implementaciones de BlockingQueue permite que se adapten a diversos escenarios de uso.
1. ¿Qué es un BlockingQueue en Java?
2. ¿Cuáles son los métodos principales de BlockingQueue que bloquean un hilo?
3. ¿Para qué es útil BlockingQueue en aplicaciones multihilo?
¡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
What are the main differences between the BlockingQueue implementations?
Can you explain how the DelayQueue works in more detail?
How does the producer-consumer example work in code?
Awesome!
Completion rate improved to 3.33
BlockingQueue y Sus Implementaciones
Desliza para mostrar el menú
Implementaciones básicas de BlockingQueue
No revisaremos cada implementación en detalle, ya que tomaría mucho tiempo y es poco probable que necesites todas. Hablaré sobre los conceptos generales y qué constructores tienen.
Ejemplo de la vida real
Imagina una fábrica donde un hilo, el productor, crea piezas, y otro hilo, el consumidor, las procesa. El productor coloca las piezas en una cola, mientras que el consumidor las recupera y procesa desde la cola. Si la cola se queda sin piezas, el consumidor espera a que el productor agregue más. Por el contrario, si la cola está llena, el productor espera a que el consumidor libere espacio.
Un poco más abajo implementaremos esta tarea en código.
Diferencias con otros tipos de colecciones
BlockingQueue proporciona sincronización automatizada, gestionando el acceso de los hilos a la cola sin requerir sincronización manual. También admite operaciones de bloqueo para agregar y recuperar elementos, una característica que no se encuentra en otras colecciones como ArrayList o LinkedList.
Implementaciones de BlockingQueue
ArrayBlockingQueue: Una cola limitada en tamaño que utiliza un arreglo para almacenar los elementos.
Main.java
123456789// Constructor with fixed capacity BlockingQueue<String> queue1 = new ArrayBlockingQueue<>(5); // Constructor with fixed capacity and fair access BlockingQueue<String> queue2 = new ArrayBlockingQueue<>(5, true); // Constructor with fixed capacity and initial collection of elements Collection<String> initialElements = java.util.Arrays.asList("One", "Two", "Three"); BlockingQueue<String> queue3 = new ArrayBlockingQueue<>(5, false, initialElements);
El parámetro true habilita una política de acceso justa proporcionando un orden FIFO para el acceso de los hilos.
LinkedBlockingQueueue: Una cola basada en nodos enlazados que puede ser restringida o irrestringida.
Main.java
123456789// Constructor without capacity bounds BlockingQueue<String> queue1 = new LinkedBlockingQueue<>(); // Constructor with fixed capacity BlockingQueue<String> queue2 = new LinkedBlockingQueue<>(5); // Constructor with initial collection of elements Collection<String> initialElements = java.util.Arrays.asList("One", "Two", "Three"); BlockingQueue<String> queue3 = new LinkedBlockingQueue<>(initialElements);
PriorityBlockingQueue: Una cola priorizada sin límites donde los elementos se recuperan de acuerdo con su orden natural o según lo especificado por un comparador.
Main.java
12345678910111213// Constructor without initial capacity (default is 11) BlockingQueue<Integer> queue1 = new PriorityBlockingQueue<>(); // Constructor with initial capacity BlockingQueue<Integer> queue2 = new PriorityBlockingQueue<>(5); // Constructor with initial capacity and comparator Comparator<Integer> comparator = Integer::compareTo; BlockingQueue<Integer> queue3 = new PriorityBlockingQueue<>(5, comparator); // Constructor with initial collection of elements Collection<Integer> initialElements = java.util.Arrays.asList(1, 3, 2); BlockingQueue<Integer> queue4 = new PriorityBlockingQueue<>(initialElements)
DelayQueue: Una cola retardada donde los elementos solo pueden ser recuperados después de que haya expirado su retardo.
DelayedElement.java
DelayQueueConstructors.java
123456789101112131415161718class DelayedElement implements Delayed { private final long expirationTime; // The time when the element will be available public DelayedElement(long delay, TimeUnit unit) { this.expirationTime = System.currentTimeMillis() + unit.toMillis(delay); } @Override public long getDelay(TimeUnit unit) { long delay = expirationTime - System.currentTimeMillis(); // Calculate the remaining delay return unit.convert(delay, TimeUnit.MILLISECONDS); // Convert the delay to the specified time unit } @Override public int compareTo(Delayed o) { return Long.compare(this.expirationTime, ((DelayedElement) o).expirationTime); } }
Este código demuestra el uso de la clase DelayedElement, que implementa la interfaz Delayed, y la cola retardada DelayQueue en Java. La clase DelayedElement define un método getDelay para calcular el tiempo de retardo restante y un método compareTo para comparar objetos según el tiempo de expiración del retardo.
El método main crea dos colas: queue1, una cola retardada vacía, y queue2, una cola inicializada con elementos que tienen un retardo de 5 y 1 segundo, respectivamente.
Los elementos en DelayQueueue se vuelven disponibles para su recuperación después de que haya transcurrido el tiempo de retardo especificado.
SynchronousQueueue: Una cola sin capacidad, donde cada operación de inserción debe esperar la correspondiente operación de extracción y viceversa.
Main.java
12345// Constructor without fair access BlockingQueue<String> queue1 = new SynchronousQueue<>(); // Constructor with fair access BlockingQueue<String> queue2 = new SynchronousQueue<>(true);
Los métodos principales de BlockingQueue:
Adición de elementos:
El método void put(E e) inserta un elemento en la cola, bloqueando el hilo si la cola está llena. Alternativamente, el método boolean offer(E e, long timeout, TimeUnit unit) intenta agregar un elemento a la cola, esperando el tiempo especificado si la cola está llena.
Main.java
1234567891011121314151617public class BlockingQueueExample { public static void main(String[] args) { BlockingQueue<String> queue = new ArrayBlockingQueue<>(2); try { queue.put("Element 1"); // Insert the first element, no blocking. queue.put("Element 2"); // Insert the second element, no blocking. // Try to add the third element with a 2-second timeout. // Since the queue is full, it will wait for 2 seconds. boolean success = queue.offer("Element 3", 2, TimeUnit.SECONDS); } catch (InterruptedException e) { e.printStackTrace(); } } }
Este ejemplo demuestra la inserción de dos elementos en una BlockingQueue sin bloqueo, seguida de un intento de agregar un tercer elemento con un tiempo de espera de 2 segundos utilizando el método offer(), el cual esperará si la cola está llena.
Recuperación de elementos:
El método E take() recupera y devuelve un elemento de la cola, bloqueando el hilo si la cola está vacía. Alternativamente, el método E poll(long timeout, TimeUnit unit) intenta recuperar un elemento de la cola, esperando el tiempo especificado si la cola está vacía.
Main.java
1234567891011121314151617181920212223public class BlockingQueueRetrievalExample { public static void main(String[] args) { BlockingQueue<String> queue = new ArrayBlockingQueue<>(2); try { // Adding elements to the queue queue.put("Element 1"); queue.put("Element 2"); // Retrieve and remove the first element, no blocking since the queue is not empty String item1 = queue.take(); // Returns "Element 1" // Attempt to retrieve and remove the next element with a 2-second timeout String item2 = queue.poll(2, TimeUnit.SECONDS); // Returns "Element 2" // Attempt to retrieve an element when the queue is empty, this will block for 2 seconds String item3 = queue.poll(2, TimeUnit.SECONDS); // Returns `null` after timeout } catch (InterruptedException e) { e.printStackTrace(); } } }
Este código agrega dos elementos a un BlockingQueue, recupera y elimina el primer elemento de inmediato, intenta recuperar el siguiente elemento con un tiempo de espera de 2 segundos, y finalmente intenta recuperar un elemento de una cola vacía, lo que resulta en un null después del tiempo de espera.
Comprobación y eliminación de elementos:
El método boolean remove(Object o) elimina el elemento especificado de la cola si está presente. Por otro lado, el método boolean contains(Object o) verifica si el elemento especificado está presente en la cola sin eliminarlo.
Main.java
1234567891011121314151617181920212223242526public class BlockingQueueCheckRemoveExample { public static void main(String[] args) { BlockingQueue<String> queue = new ArrayBlockingQueue<>(2); try { // Adding elements to the queue queue.put("Element 1"); queue.put("Element 2"); // Check if "Element 1" is in the queue, should return `true` boolean containsElement1 = queue.contains("Element 1"); // true // Remove "Element 1" from the queue, should return `true` boolean removedElement1 = queue.remove("Element 1"); // true // Check if "Element 1" is still in the queue, should return `false` boolean containsElement1AfterRemoval = queue.contains("Element 1"); // false // Try to remove an element that is not in the queue, should return `false` boolean removedElement3 = queue.remove("Element 3"); // false } catch (InterruptedException e) { e.printStackTrace(); } } }
Este código agrega dos elementos a un BlockingQueue, verifica la presencia de "Element 1", lo elimina, vuelve a comprobar para confirmar su eliminación y luego intenta eliminar un elemento inexistente.
Consulta el estado de la cola:
El método int size() devuelve el número de elementos que hay actualmente en la cola. Para determinar si la cola está vacía, se puede utilizar el método boolean isEmpty(), que comprueba si la cola no contiene elementos. Para colas con capacidad fija, el método int remainingCapacity() proporciona el número de espacios disponibles restantes en la cola.
Main.java
123456789101112131415161718192021222324252627282930public class BlockingQueueCapacityExample { public static void main(String[] args) { BlockingQueue<String> queue = new ArrayBlockingQueue<>(3); try { // Adding elements to the queue queue.put("Element 1"); queue.put("Element 2"); // Get the number of elements in the queue int currentSize = queue.size(); // 2 // Check if the queue is empty boolean isQueueEmpty = queue.isEmpty(); // false // Get the remaining capacity in the queue int remainingSpace = queue.remainingCapacity(); // 1 // Add another element to fill the queue queue.put("Element 3"); // Check the size and remaining capacity after adding the third element currentSize = queue.size(); // 3 remainingSpace = queue.remainingCapacity(); // 0 } catch (InterruptedException e) { e.printStackTrace(); } } }
Este código agrega elementos a un BlockingQueue, verifica el tamaño actual, comprueba si la cola está vacía y determina la capacidad restante, luego actualiza estos valores después de llenar la cola completamente.
Realización de un ejemplo de la vida real en código
😭 Limitaciones
Una limitación clave es el rendimiento: debido a las operaciones de bloqueo involucradas, el rendimiento puede verse reducido en comparación con las colecciones no sincronizadas. Además, los recursos pueden convertirse en un problema, ya que las colas grandes requieren más memoria y tiempo de CPU para gestionar los bloqueos y los procesos de sincronización.
💪 Ventajas
Por el lado positivo, el sistema es seguro en entornos multihilo, ofreciendo comunicación segura entre hilos sin requerir una gestión manual de la sincronización. También simplifica el código al evitar construcciones complejas de sincronización y bloqueo. Además, la flexibilidad de las diferentes implementaciones de BlockingQueue permite que se adapten a diversos escenarios de uso.
1. ¿Qué es un BlockingQueue en Java?
2. ¿Cuáles son los métodos principales de BlockingQueue que bloquean un hilo?
3. ¿Para qué es útil BlockingQueue en aplicaciones multihilo?
¡Gracias por tus comentarios!