usar sirve settitle que para codigo java multithreading collections iterator thread-safety

java - sirve - Iteración segura para subprocesos sobre una colección



settitle java (8)

Depende de tu modelo de acceso. Si tiene poca concurrencia y escrituras frecuentes, 1 tendrá el mejor rendimiento. Si tiene una alta concurrencia con las escrituras poco frecuentes, 3 tendrá el mejor rendimiento. La opción 2 tendrá un mal desempeño en casi todos los casos.

foreach llama a iterator() , por lo que se aplican exactamente las mismas cosas.

Todos sabemos que cuando usamos Collections.synchronizedXXX (por ejemplo, synchronizedSet() ) obtenemos una "vista" sincronizada de la colección subyacente.

Sin embargo, el documento de estos métodos de generación de envoltorio indica que debemos sincronizar explícitamente en la colección cuando se iteran las colecciones utilizando un iterador.

¿Qué opción eliges para resolver este problema?

Solo puedo ver los siguientes enfoques:

  1. Hazlo como indica la documentación: sincronizar en la colección.
  2. Clone la colección antes de llamar al iterator()
  3. Utilice una colección cuyo iterador sea seguro para subprocesos (solo conozco CopyOnWriteArrayList / Set)

Y como pregunta adicional: cuando se utiliza una vista sincronizada, ¿es seguro el uso de foreach / Iterable thread-safe?


Depende del resultado que se necesita para lograr la clonación / copia / toArray (), la nueva ArrayList (..) y los "me gusta" obtienen una instantánea y no bloquean la colección. El uso sincronizado (recopilación) y la iteración para asegurar al final de la iteración no sería una modificación, es decir, bloquearla de manera efectiva.

nota al margen: (aArray () generalmente se prefiere con algunas excepciones cuando internamente necesita crear un ArrayList temporal). Además, tenga en cuenta que todo lo que no sea toArray () también debe incluirse en la sincronización (recopilación), siempre que se use utilizando Collections.synchronizedXXX.


Es posible que no esté totalmente de acuerdo con sus requisitos, pero si no los conoce, consulte las google-collections teniendo en cuenta "Favorecer la inmutabilidad".


Esta pregunta es bastante antigua (lo siento, llego un poco tarde ...) pero todavía quiero agregar mi respuesta.

Elegiría su segunda opción (es decir, Clonar la colección antes de llamar a iterador ()) pero con un giro importante.

Suponiendo que quiere iterar usando iterador, no tiene que copiar la colección antes de llamar a .iterator () y negar de alguna manera (estoy usando el término "negar" libremente) la idea del patrón de iterador, pero podría escribir un "ThreadSafeIterator".

Funcionaría en la misma premisa, copiando la Colección, pero sin dejar que la clase iterante supiera, eso fue exactamente lo que hizo. Tal iterador podría tener este aspecto:

class ThreadSafeIterator<T> implements Iterator<T> { private final Queue<T> clients; private T currentElement; private final Collection<T> source; AsynchronousIterator(final Collection<T> collection) { clients = new LinkedList<>(collection); this.source = collection; } @Override public boolean hasNext() { return clients.peek() != null; } @Override public T next() { currentElement = clients.poll(); return currentElement; } @Override public void remove() { synchronized(source) { source.remove(currentElement); } } }

Además de esto, puede utilizar la Clase de Semaphore para garantizar la seguridad de los hilos o algo así. Pero tome el método de eliminación con un grano de sal.

El punto es que, al usar un iterador de este tipo, nadie, ni la iteración ni la clase iterada (es una palabra real) tiene que preocuparse por la seguridad del hilo.


Las tres opciones funcionarán. Elegir el adecuado para su situación dependerá de cuál sea su situación.

CopyOnWriteArrayList funcionará si desea una implementación de lista y no le importa que el almacenamiento subyacente se copie cada vez que escriba. Esto es bastante bueno para el rendimiento, siempre y cuando no tengas colecciones muy grandes.

ConcurrentHashMap o " ConcurrentHashSet " (utilizando Collections.newSetFromMap ) funcionará si necesita un interfaz de Map o Set , obviamente, no obtendrá acceso aleatorio de esta manera. Uno genial! Lo importante de estos dos es que funcionarán bien con grandes conjuntos de datos: cuando se muta, simplemente copian pequeños fragmentos del almacenamiento de datos subyacente.


Podría usar una de las colecciones más nuevas agregadas en Java 5.0 que admiten el acceso simultáneo mientras se realiza la iteración. Otro enfoque es tomar una copia usando toArray, que es seguro para subprocesos (durante la copia).

Collection<String> words = ... // enhanced for loop over an array. for(String word: words.toArray(new String[0])) { }


Realmente ya respondiste tu pregunta extra: no, usar un bucle mejorado para no es seguro, porque usa un iterador.

En cuanto a cuál es el enfoque más apropiado, realmente depende de cómo se desarrolle su contexto:

  • ¿Son las escrituras muy infrecuentes? Si es así, CopyOnWriteArrayList puede ser lo más apropiado.
  • ¿Es la colección razonablemente pequeña y la iteración rápida? (es decir, no está haciendo mucho trabajo en el bucle) Si es así, la sincronización puede estar bien, especialmente si esto no sucede con demasiada frecuencia (es decir, no tendrá mucha disputa por la colección).
  • Si está haciendo mucho trabajo y no quiere bloquear otros subprocesos que funcionan al mismo tiempo, el éxito de clonar la colección puede ser aceptable.

Sugiero eliminar Collections.synchronizedXXX y manejar todo el bloqueo de manera uniforme en el código del cliente. Las colecciones básicas no admiten el tipo de operaciones compuestas útiles en el código de subprocesos, e incluso si utiliza java.util.concurrent.* El código es más difícil. Sugiero mantener tanto código como sea posible sin hilos. Mantenga al mínimo el código difícil y propenso a errores, seguro de subprocesos (si tenemos mucha suerte).