Colas sin bloqueo

Implementaciones de cola seguras para subprocesos y, lo que es más importante, sin bloqueo en nodos vinculados.

ConcurrentLinkedQueue<E> : utiliza un algoritmo sin esperas adaptado para trabajar con el recolector de elementos no utilizados. Este algoritmo es bastante eficiente y muy rápido, ya que se basa en CAS. El método size() puede ejecutarse durante mucho tiempo, por lo que es mejor no tirar de él todo el tiempo.

ConcurrentLinkedDeque<E> : Deque significa cola de doble extremo. Esto significa que los datos se pueden agregar y extraer de ambos lados. En consecuencia, la clase admite ambos modos de operación: FIFO (primero en entrar, primero en salir) y LIFO (último en entrar, primero en salir).

En la práctica, se debe usar ConcurrentLinkedDeque si LIFO es absolutamente necesario, ya que debido a la bidireccionalidad de los nodos, esta clase pierde la mitad en rendimiento en comparación con ConcurrentLinkedQueue .

import java.util.concurrent.ConcurrentLinkedQueue;

public class  ConcurrentLinkedQueueExample {
   public static void main(String[] args) {
       ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<>();

       Thread producer = new Thread(new Producer(queue));
       Thread consumer = new Thread(new Consumer(queue));

       producer.start();
       consumer.start();
   }
}

class Producer implements Runnable {

   ConcurrentLinkedQueue<String> queue;
   Producer(ConcurrentLinkedQueue<String> queue){
       this.queue = queue;
   }
   public void run() {
       System.out.println("Class for adding items to the queue");
       try {
           for (int i = 1; i < 5; i++) {
               queue.add("Item #" + i);
               System.out.println("Added: Item #" + i);
               Thread.sleep(300);
           }
       } catch (InterruptedException ex) {
           ex.printStackTrace();
           Thread.currentThread().interrupt();
       }
   }
}

class Consumer implements Runnable {

   ConcurrentLinkedQueue<String> queue;
   Consumer(ConcurrentLinkedQueue<String> queue){
       this.queue = queue;
   }

   public void run() {
       String str;
       System.out.println("Class for getting items from the queue");
       for (int x = 0; x < 5; x++) {
           while ((str = queue.poll()) != null) {
               System.out.println("Pulled out: " + str);
           }
           try {
               Thread.sleep(600);
           } catch (InterruptedException ex) {
               ex.printStackTrace();
               Thread.currentThread().interrupt();
           }
       }
   }
}

Colas de bloqueo

Interfaz BlockingQueue<E> : si hay muchos datos, ConcurrentLinkedQueue no es suficiente.

Cuando los subprocesos no hacen su trabajo, puede obtener fácilmente una excepción OutOfMemmoryException . Y para que no surjan tales casos, tenemos un BlockingQueue para trabajar con la presencia de diferentes métodos para llenar y trabajar con la cola y bloqueos condicionales.

BlockingQueue no reconoce elementos nulos y lanza una NullPointerException cuando intenta agregar u obtener dicho elemento. El método de encuesta devuelve un elemento nulo si no se ha colocado ningún elemento en la cola dentro del tiempo de espera.

Implementaciones de BlockingQueue<E>

Echemos un vistazo más de cerca a cada una de nuestras implementaciones de BlockingQueue :

ArrayBlockingQueue<E> es una clase de cola de bloqueo creada en el búfer de anillo clásico. Aquí tenemos la oportunidad de gestionar la “honestidad” de las cerraduras. Si fair=false (predeterminado), no se garantiza el orden de los subprocesos.

DelayQueue<E extends Delayed> es una clase que te permite extraer elementos de la cola solo después de un cierto retraso, definido en cada elemento a través del método getDelay de la interfaz Delayed .

LinkedBlockingQueue<E> es una cola de bloqueo en nodos vinculados, implementada en el algoritmo de "dos colas de bloqueo": el primer bloqueo es para agregar, el segundo es para extraer un elemento de la cola. Debido a los bloqueos, en comparación con ArrayBlockingQueue , esta clase tiene un alto rendimiento, pero requiere más memoria. El tamaño de la cola se establece a través del constructor y es igual a Integer.MAX_VALUE de forma predeterminada.

PriorityBlockingQueue<E> es un contenedor de subprocesos múltiples sobre PriorityQueue . El Comparador es responsable de la lógica por la cual se agregará el elemento. El elemento más pequeño sale primero.

SynchronousQueue<E> : la cola funciona según el principio FIFO (primero en entrar, primero en salir). Cada operación de inserción bloquea el subproceso "Productor" hasta que el subproceso "Consumidor" extrae el elemento de la cola y viceversa, el "Consumidor" esperará hasta que el "Productor" inserte el elemento.

BlockingDeque<E> es una interfaz que describe métodos adicionales para una cola de bloqueo bidireccional. Los datos se pueden insertar y extraer de ambos lados de la cola.

LinkedBlockingDeque<E> es una cola de bloqueo bidireccional en nodos vinculados, implementada como una lista bidireccional simple con un bloqueo. El tamaño de la cola se establece a través del constructor y es igual a Integer.MAX_VALUE de forma predeterminada.

TransferQueue<E> : la interfaz es interesante porque cuando se agrega un elemento a la cola, es posible bloquear el subproceso productor de inserción hasta que otro subproceso consumidor extraiga el elemento de la cola. También puede agregar una verificación para un tiempo de espera específico o establecer una verificación para los correos electrónicos pendientes de los consumidores . Como resultado, obtenemos un mecanismo de transferencia de datos con soporte para mensajes asíncronos y síncronos.

LinkedTransferQueue<E> es una implementación de TransferQueue basada en el algoritmo Dual Queues with Slack. Hace un uso intensivo de CAS (ver arriba) y estacionamiento de subprocesos cuando está inactivo.