LinkedBlockingQueue vs ConcurrentLinkedQueue

1. Introducción

LinkedBlockingQueue y ConcurrentLinkedQueue son las dos colas simultáneas más utilizadas en Java . Aunque ambas colas se utilizan a menudo como una estructura de datos concurrente, existen características sutiles y diferencias de comportamiento entre ellas.

En este breve tutorial, analizaremos estas dos colas y explicaremos sus similitudes y diferencias.

2. LinkedBlockingQueue

El LinkedBlockingQueue es un delimitada opcionalmente aplicación cola de bloqueo, lo que significa que el tamaño de la cola se puede especificar si es necesario.

Creemos una LinkedBlockingQueue que puede contener hasta 100 elementos:

BlockingQueue boundedQueue = new LinkedBlockingQueue(100);

También podemos crear una LinkedBlockingQueue ilimitada simplemente sin especificar el tamaño:

BlockingQueue unboundedQueue = new LinkedBlockingQueue();

Una cola ilimitada implica que el tamaño de la cola no se especifica durante la creación. Por lo tanto, la cola puede crecer dinámicamente a medida que se le agregan elementos. Sin embargo, si no queda memoria, la cola arroja un java.lang.OutOfMemoryError.

También podemos crear una LinkedBlockingQueue a partir de una colección existente:

Collection listOfNumbers = Arrays.asList(1,2,3,4,5); BlockingQueue queue = new LinkedBlockingQueue(listOfNumbers);

La clase LinkedBlockingQueue implementa la interfaz BlockingQueue , que le proporciona la naturaleza de bloqueo .

Una cola de bloqueo indica que la cola bloquea el subproceso de acceso si está llena (cuando la cola está limitada) o se vacía. Si la cola está llena, agregar un nuevo elemento bloqueará el hilo de acceso a menos que haya espacio disponible para el nuevo elemento. Del mismo modo, si la cola está vacía, acceder a un elemento bloquea el hilo de llamada:

ExecutorService executorService = Executors.newFixedThreadPool(1); LinkedBlockingQueue queue = new LinkedBlockingQueue(); executorService.submit(() -> { try { queue.take(); } catch (InterruptedException e) { // exception handling } });

En el fragmento de código anterior, estamos accediendo a una cola vacía. Por lo tanto, el método take bloquea el hilo de llamada.

La función de bloqueo de LinkedBlockingQueue está asociada con algunos costos. Este costo se debe a que cada operación de venta o toma se disputa entre el productor o el consumidor. Por lo tanto, en escenarios con muchos productores y consumidores, poner y tomar acciones podría ser más lento.

3. ConcurrentLinkedQueue

Una ConcurrentLinkedQueue es una cola ilimitada, segura para subprocesos y sin bloqueo.

Creemos una ConcurrentLinkedQueue vacía :

ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue();

También podemos crear un ConcurrentLinkedQueue a partir de una colección existente:

Collection listOfNumbers = Arrays.asList(1,2,3,4,5); ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue(listOfNumbers);

A diferencia de un LinkedBlockingQueue, un ConcurrentLinkedQueue es una cola de no bloqueo . Por lo tanto, no bloquea un hilo una vez que la cola está vacía. En cambio, devuelve nulo . Dado que no tiene límites, arrojará un java.lang.OutOfMemoryError si no hay memoria adicional para agregar nuevos elementos.

Además de no ser bloqueante, ConcurrentLinkedQueue tiene una funcionalidad adicional.

En cualquier escenario productor-consumidor, los consumidores no se contentarán con los productores; sin embargo, varios productores competirán entre sí:

int element = 1; ExecutorService executorService = Executors.newFixedThreadPool(2); ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue(); Runnable offerTask = () -> queue.offer(element); Callable pollTask = () -> { while (queue.peek() != null) { return queue.poll().intValue(); } return null; }; executorService.submit(offerTask); Future returnedElement = executorService.submit(pollTask); assertThat(returnedElement.get().intValue(), is(equalTo(element))); 

La primera tarea, offerTask , agrega un elemento a la cola y la segunda tarea, pollTask, recupera un elemento de la cola. La tarea de sondeo también comprueba primero la cola en busca de un elemento, ya que ConcurrentLinkedQueue no es bloqueante y puede devolver un valor nulo .

4. Similitudes

Tanto LinkedBlockingQueue como ConcurrentLinkedQueue son implementaciones de colas y comparten algunas características comunes. Analicemos las similitudes de estas dos colas:

  1. Ambos implementan la interfaz de cola
  2. Ambos usan nodos vinculados para almacenar sus elementos.
  3. Ambos son adecuados para escenarios de acceso concurrente

5. Diferencias

Aunque ambas colas tienen ciertas similitudes, también existen diferencias sustanciales de características:

Característica LinkedBlockingQueue ConcurrentLinkedQueue
Bloqueo de la naturaleza Es una cola de bloqueo e implementa la interfaz BlockingQueue Es una cola sin bloqueo y no implementa la interfaz BlockingQueue
Tamaño de la cola Es una cola limitada opcionalmente, lo que significa que hay disposiciones para definir el tamaño de la cola durante la creación. Es una cola ilimitada y no hay ninguna disposición para especificar el tamaño de la cola durante la creación.
Bloquear la naturaleza Es una cola basada en bloqueos Es una cola sin candados
Algoritmo Implementa su bloqueo basado en el algoritmo de cola de dos bloqueos. Se basa en el algoritmo de Michael & Scott para colas sin bloqueo y sin bloqueo
Implementación En el mecanismo del algoritmo de cola de dos bloqueos , LinkedBlockingQueue usa dos bloqueos diferentes: el putLock y el takeLock . Las operaciones de poner / tomar usan el primer tipo de bloqueo y las operaciones de tomar / sondear usan el otro tipo de bloqueo Utiliza CAS (Compare-And-Swap ) para sus operaciones
Comportamiento de bloqueo Es una cola de bloqueo. Entonces, bloquea los hilos de acceso cuando la cola está vacía No bloquea el hilo de acceso cuando la cola está vacía y devuelve nulo

6. Conclusión

En este artículo, aprendimos sobre LinkedBlockingQueue y ConcurrentLinkedQueue.

Primero, discutimos individualmente estas dos implementaciones de colas y algunas de sus características. Luego, vimos las similitudes entre estas dos implementaciones de cola. Finalmente, exploramos las diferencias entre estas dos implementaciones de colas.

Como siempre, el código fuente de los ejemplos está disponible en GitHub.