Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Aprende BlockingQueue y Sus Implementaciones | Colecciones Sincronizadas
Multithreading en Java

bookBlockingQueue 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.

Note
Nota

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

Main.java

copy
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);
Note
Nota

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

Main.java

copy
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

Main.java

copy
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

DelayedElement.java

DelayQueueConstructors.java

DelayQueueConstructors.java

copy
123456789101112131415161718
class 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

Main.java

copy
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

Main.java

copy
1234567891011121314151617
public 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

Main.java

copy
1234567891011121314151617181920212223
public 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

Main.java

copy
1234567891011121314151617181920212223242526
public 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

Main.java

copy
123456789101112131415161718192021222324252627282930
public 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?

question mark

¿Qué es un BlockingQueue en Java?

Select the correct answer

question mark

¿Cuáles son los métodos principales de BlockingQueue que bloquean un hilo?

Select the correct answer

question mark

¿Para qué es útil BlockingQueue en aplicaciones multihilo?

Select the correct answer

¿Todo estuvo claro?

¿Cómo podemos mejorarlo?

¡Gracias por tus comentarios!

Sección 2. Capítulo 3

Pregunte a AI

expand

Pregunte a AI

ChatGPT

Pregunte lo que quiera o pruebe una de las preguntas sugeridas para comenzar nuestra charla

Suggested prompts:

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

bookBlockingQueue 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.

Note
Nota

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

Main.java

copy
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);
Note
Nota

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

Main.java

copy
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

Main.java

copy
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

DelayedElement.java

DelayQueueConstructors.java

DelayQueueConstructors.java

copy
123456789101112131415161718
class 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

Main.java

copy
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

Main.java

copy
1234567891011121314151617
public 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

Main.java

copy
1234567891011121314151617181920212223
public 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

Main.java

copy
1234567891011121314151617181920212223242526
public 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

Main.java

copy
123456789101112131415161718192021222324252627282930
public 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?

question mark

¿Qué es un BlockingQueue en Java?

Select the correct answer

question mark

¿Cuáles son los métodos principales de BlockingQueue que bloquean un hilo?

Select the correct answer

question mark

¿Para qué es útil BlockingQueue en aplicaciones multihilo?

Select the correct answer

¿Todo estuvo claro?

¿Cómo podemos mejorarlo?

¡Gracias por tus comentarios!

Sección 2. Capítulo 3
some-alt