1) La CopyOnWriteArraySet
implementación es bastante simple: básicamente tiene una lista de elementos en una matriz y, al cambiar la lista, copia la matriz. Las iteraciones y otros accesos que se están ejecutando en este momento continúan con la matriz anterior, evitando la necesidad de sincronización entre lectores y escritores (aunque la escritura misma debe sincronizarse). Las operaciones de configuración normalmente rápidas (especialmente contains()
) son bastante lentas aquí, ya que las matrices se buscarán en tiempo lineal.
Use esto solo para conjuntos realmente pequeños que se leerán (iterarán) con frecuencia y se cambiarán raramente. (Swing listener-sets sería un ejemplo, pero estos no son realmente sets, y de todos modos solo deberían usarse desde el EDT).
2) Collections.synchronizedSet
simplemente envolverá un bloque sincronizado alrededor de cada método del conjunto original. No debe acceder al conjunto original directamente. Esto significa que no se pueden ejecutar simultáneamente dos métodos del conjunto (uno se bloqueará hasta que el otro termine): esto es seguro para subprocesos, pero no tendrá concurrencia si varios subprocesos realmente están usando el conjunto. Si usa el iterador, generalmente aún necesita sincronizar externamente para evitar ConcurrentModificationExceptions al modificar el conjunto entre llamadas de iterador. El rendimiento será como el rendimiento del conjunto original (pero con cierta sobrecarga de sincronización y bloqueo si se usa simultáneamente).
Use esto si solo tiene poca concurrencia y desea asegurarse de que todos los cambios sean visibles de inmediato para los otros subprocesos.
3) ConcurrentSkipListSet
es la SortedSet
implementación concurrente , con la mayoría de las operaciones básicas en O (log n). Permite agregar / eliminar y leer / iterar simultáneamente, donde la iteración puede o no informar sobre los cambios desde que se creó el iterador. Las operaciones masivas son simplemente llamadas individuales múltiples, y no atómicamente; otros subprocesos pueden observar solo algunas de ellas.
Obviamente, puede usar esto solo si tiene un orden total en sus elementos. Esto parece un candidato ideal para situaciones de alta concurrencia, para conjuntos no demasiado grandes (debido a la O (log n)).
4) Para el ConcurrentHashMap
(y el Conjunto derivado de él): Aquí las opciones más básicas son (en promedio, si tiene un buen y rápido hashCode()
) en O (1) (pero podría degenerar en O (n)), como para HashMap / HashSet. Hay una concurrencia limitada para la escritura (la tabla está particionada, y el acceso de escritura se sincronizará en la partición necesaria), mientras que el acceso de lectura es totalmente concurrente para sí mismo y los hilos de escritura (pero es posible que aún no vean los resultados de los cambios que se están realizando actualmente escrito). El iterador puede ver o no cambios desde que se creó, y las operaciones masivas no son atómicas. El cambio de tamaño es lento (como para HashMap / HashSet), por lo tanto, trate de evitar esto estimando el tamaño necesario en la creación (y usando aproximadamente 1/3 más de eso, ya que cambia el tamaño cuando está 3/4 lleno).
Use esto cuando tenga conjuntos grandes, una función hash buena (y rápida) y pueda estimar el tamaño del conjunto y la concurrencia necesaria antes de crear el mapa.
5) ¿Hay otras implementaciones de mapas concurrentes que uno podría usar aquí?
ConcurrentHashMap
conjunto basado, "intenta evitar esto estimando el tamaño necesario en la creación". El tamaño que le dé al mapa debe ser más de un 33% mayor que su estimación (o valor conocido), ya que el conjunto cambia de tamaño al 75% de carga. Yo usoexpectedSize + 4 / 3 + 1
+
está destinado a ser un*
?expectedSize * 4 / 3 + 1
ConcurrentMap
(oHashMap
) en Java 8 si el número de entradas que se asignan al mismo depósito alcanza el valor umbral (creo que es 16), entonces la lista se cambia a un árbol de búsqueda binario (árbol rojo-negro para ser precisos) y en ese caso buscar el tiempo seríaO(lg n)
y noO(n)
.Es posible combinar el
contains()
rendimiento deHashSet
con las propiedades relacionadas con la concurrencia deCopyOnWriteArraySet
mediante el usoAtomicReference<Set>
y la sustitución de todo el conjunto en cada modificación.El boceto de implementación:
fuente
AtomicReference
marca el valor volátil. Significa que se asegura de que ningún subproceso esté leyendo datos obsoletos y proporcionahappens-before
garantía ya que el compilador no puede reordenar el código. Pero si soloAtomicReference
se utilizan los métodos get / set de , entonces estamos marcando nuestra variable volátil de una manera elegante.abstract
, aparentemente para evitar tener que escribir varios de los métodos. Me puse a agregarlos, pero me encontré con un obstáculoiterator()
. No sé cómo mantener un iterador sobre esto sin romper el modelo. Parece que siempre tengo que pasar por elref
, y podría obtener un conjunto subyacente diferente cada vez, lo que requiere obtener un nuevo iterador en el conjunto subyacente, lo cual es inútil para mí, ya que comenzará con el elemento cero. Alguna idea?Si los Javadocs no ayudan, probablemente debería encontrar un libro o artículo para leer sobre las estructuras de datos. De un vistazo:
fuente
Conjunto concurrente de referencias débiles
Otro giro es un conjunto seguro de hilos de referencias débiles .
Tal conjunto es útil para rastrear suscriptores en un escenario pub-sub . Cuando un suscriptor está fuera de alcance en otros lugares y, por lo tanto, se dirige a convertirse en un candidato para la recolección de basura, no es necesario que el suscriptor se moleste en darse de baja con gracia. La referencia débil permite al suscriptor completar su transición para ser candidato para la recolección de basura. Cuando finalmente se recolecta la basura, se elimina la entrada en el conjunto.
Si bien este conjunto no se proporciona directamente con las clases agrupadas, puede crear uno con algunas llamadas.
Primero, comenzamos haciendo una
Set
referencia débil aprovechando laWeakHashMap
clase. Esto se muestra en la documentación de la clase paraCollections.newSetFromMap
.El valor del mapa
Boolean
es irrelevante aquí, ya que la clave del mapa constituye nuestroSet
.En un escenario como pub-sub, necesitamos seguridad de subprocesos si los suscriptores y editores están operando en subprocesos separados (muy probablemente el caso).
Vaya un paso más allá envolviéndolo como un conjunto sincronizado para que este conjunto sea seguro para subprocesos. Alimentar en una llamada a
Collections.synchronizedSet
.Ahora podemos agregar y eliminar suscriptores de nuestro resultado
Set
. Y cualquier suscriptor "desaparecido" eventualmente será eliminado automáticamente después de que se ejecute la recolección de basura. Cuando ocurre esta ejecución depende de la implementación del recolector de basura de su JVM, y depende de la situación de tiempo de ejecución en este momento. Para una discusión y un ejemplo de cuándo y cómo el subyacenteWeakHashMap
borra las entradas caducadas, vea esta pregunta, * ¿WeakHashMap está en constante crecimiento o borra las claves de basura? * .fuente