6 Concurrent-aware collections¶
Concurrent-aware collections¶
Para facilitar el uso de estructuras de datos en programas concurrentes, Java proporciona una serie de interfaces y de clases para estructuras de datos diseñadas específicamente para ser usados en programas multihilo sin que se produzcan errores de inconsistencia de datos.
Concurrent-aware
Diseñado específicamente para que uso concurrente
Se dice que dichas clases son no solo thread-safe, sino también concurrent-aware (adecuadas para uso concurrente). Estas clases pueden clasificarse en los siguientes grupos:
- Colecciones concurrentes que usan cerrojos: Aseguran la integridad haciendo uso de varios cerrojos de sincronización independientes. Un ejemplo de este tipo de colecciones es
LinkedBlockingQueue
. Suelen tener dos modos de funcionamiento:- Modo bloqueante: Incluye operaciones para añadir y eliminar datos, de manera que si la operación no puede ser realizada inmediatamente, ya sea porque la colección está llena o vacía, el hilo que realiza la llamada a la operación será bloqueado hasta que la operación pueda llevarse a cabo.
- Modo no bloqueante: Incluye operaciones para añadir y eliminar datos, pero en este caso si la operación no se puede realizar inmediatamente, la operación retorna el valor
null
o lanza una excepción, y el hilo que realizó la llamada NO será bloqueado.
- Colecciones concurrentes que no usan cerrojos (lock-free): Aseguran la integridad sin tener que usan ningún tipo de cerrojo, sino operaciones CAS (compare and swap). Por su naturaleza sólo pueden funcionar en modo no bloqueante. Un ejemplo de este tipo de colecciones es
ConcurrentLinkedQueue
.
Aunque las colecciones concurrentes que usan cerrojos se pueden usar en modo no bloqueante, en ese caso es más óptimo usar colecciones concurrentes que no usen cerrojos (lock-free).
Las colecciones concurrentes bloqueantes están recomendadas en aquellos casos en los que los consumidores y los productores trabajen de forma conjunta. En ese caso, por ejemplo, los consumidores pueden llegar a vaciar la cola, en cuyo caso debe bloquearse hasta que algún productor agregue algún elemento a la misma.
Sin embargo, si los productores y los consumidores no trabajan sobre la colección conjuntamente, es decir, si los productores sólo compiten entre ellos o los consumidores sólo compiten entre ellos, no hay necesidad de bloqueo, y por tanto se recomienda usar una colección concurrente que no use cerrojo.
El paquete java.util.concurrent
define una serie de interfaces para colecciones que puedan funcionar en modo bloqueante:
BlockingQueue
: Define una estructura de datos cola FIFO concurrent-aware. Es implementada por las clasesArrayBlockingQueue
,LinkedBlockingQueue
yPriorityBlockingQueue
.TransferQueue
: Define una estructura de datos cola FIFO concurrent-aware en las que los productores (los que insertan datos) pueden decidir esperar a que los consumidores (los que extraen datos) reciban los elementos. Es implementada por la claseLinkedTransferQueue
.BlockingDeque
: Define una estructura de datos cola LIFO y FIFO concurrent-aware (con inserciones y extracciones en ambos extremos).
También define una serie de interfaces para colecciones que funcionen sólo en modo no bloqueante:
ConcurrentMap
: Define una estructura de datos mapa concurrent-aware. Es implementada por las clasesConcurrentHashMap
yConcurrentSkipListMap
.ConcurrentNavigableMap
: Define una estructura de datos de mapa navegable concurrent-aware. Es implementado por la claseConcurrentSkipListMap
.