framework - La mejor forma de controlar el acceso simultáneo a las colecciones de Java
hashset java (7)
CopyOnWriteArrayList vale la pena mirar. Está diseñado para una lista que generalmente se lee. Cada escritura haría que creara una nueva matriz detrás de las cubiertas para que las iteraciones a través de la matriz no obtuvieran una ConcurrentModificationException
¿Debo usar una colección de vectores sincronizados anterior, ArrayList con acceso sincronizado o Collections.synchronizedList o alguna otra solución para el acceso concurrente?
No veo mi pregunta en Preguntas relacionadas ni en mi búsqueda (¿ Hacer que tus colecciones sean seguras para hilos? No es lo mismo).
Recientemente, tuve que hacer una especie de pruebas unitarias en partes de la GUI de nuestra aplicación (básicamente usando API para crear marcos, agregar objetos, etc.). Debido a que estas operaciones son llamadas mucho más rápido que por un usuario, muestra una serie de problemas con los métodos que intentan acceder a recursos que aún no se han creado o que ya no se eliminaron.
Un problema particular, que sucede en el EDT, proviene de recorrer una lista vinculada de vistas mientras se altera en otro hilo (obteniendo una ConcurrentModificationException entre otros problemas). No me preguntes por qué era una lista vinculada en lugar de una simple lista de arreglos (incluso menos que en general tenemos 0 o 1 vista dentro ...), así que tomé ArrayList más común en mi pregunta (ya que tiene una primo mayor).
De todos modos, no muy familiarizado con los problemas de concurrencia, busqué un poco de información, y me pregunté qué elegir entre el Vector viejo (y probablemente obsoleto) (que tiene operaciones sincronizadas por diseño), ArrayList con un synchronized (myList) { }
alrededor secciones críticas (operaciones de agregar / quitar / caminar) o usando una lista devuelta por Collections.synchronizedList (ni siquiera está seguro de cómo usar esta última).
Finalmente elegí la segunda opción, porque otro error de diseño fue exponer el objeto (método getViewList () ...) en lugar de proporcionar mecanismos para usarlo.
Pero, ¿cuáles son los pros y los contras de los otros enfoques?
[EDITAR] Muchos buenos consejos aquí, difíciles de seleccionar. Elegiré los más detallados y proporcionaré enlaces / alimentos para pensamientos ... :-) También me gusta el de Darron.
Para resumir:
- Como sospechaba, Vector (y su gemelo malvado, Hashtable también, probablemente) es en gran parte obsoleto, he visto gente diciendo que su viejo diseño no es tan bueno como las colecciones más nuevas, más allá de la lentitud de la sincronización obligada incluso en el entorno de un único hilo . Si lo mantenemos, es principalmente porque las bibliotecas antiguas (y partes de la API de Java) aún lo usan.
- A diferencia de lo que yo pensaba, Collections.synchronizedXxxx no son más modernos que Vector (parecen ser contemporáneos a Collections, es decir, Java 1.2!) Y no son mejores, en realidad. Bueno saber. En resumen, debería evitarlos también.
- La sincronización manual parece ser una buena solución después de todo. Puede haber problemas de rendimiento, pero en mi caso no es crítico: operaciones realizadas en acciones del usuario, colección pequeña, sin uso frecuente.
- El paquete java.util.concurrent vale la pena tenerlo en cuenta, especialmente los métodos CopyOnWrite.
Espero haberlo hecho bien ... :-)
La solución más segura es evitar el acceso simultáneo a datos compartidos. En lugar de tener subprocesos no EDT que operen con los mismos datos, pídales que llamen a SwingUtilities.invokeLater()
con un Runnable que realice las modificaciones.
En serio, la concurrencia de datos compartidos es un nido de víboras en el que nunca sabrás si no hay otra condición de carrera o un punto muerto escondido en algún lado, esperando que te muerda el culo en la peor ocasión posible.
No creo que el Iterator
devuelto por un vector esté de ninguna manera sincronizado, lo que significa que Vector
no puede garantizar (por sí solo) que un hilo no esté modificando el conjunto subyacente mientras que otro hilo lo itera.
Creo que para garantizar que la iteración sea segura para subprocesos, tendrá que manejar la sincronización por su cuenta. Suponiendo que el mismo Vector / Lista / objeto es compartido por múltiples hilos (y por lo tanto conduce a sus problemas), ¿no puede simplemente sincronizar en ese objeto?
No puedo pensar en una buena razón para preferir Vector
sobre ArrayList
. Las operaciones de lista en un Vector
están sincronizadas, lo que significa que varios hilos pueden modificarlo de manera segura. Y como usted dice, las operaciones de ArrayList se pueden sincronizar usando Collections.synchronizedList
.
Recuerde que incluso cuando usa listas sincronizadas, aún encontrará ConcurrentModificationExceptions
si itera sobre la colección mientras está siendo modificada por otra cadena. Entonces, es importante coordinar ese acceso externamente (su segunda opción).
Una técnica común utilizada para evitar el problema de iteración es iterar sobre copias inmutables de la colección. Ver Collections.unmodifiableList
Recomiendo encarecidamente el libro " Concurrencia de Java en la práctica ".
Cada una de las opciones tiene ventajas / desventajas:
- Vector - considerado "obsoleto". Puede recibir menos atención y correcciones de errores que otras colecciones convencionales.
- Sus propios bloques de sincronización: muy fácil de obtener incorrecto. A menudo da un rendimiento más pobre que las opciones a continuación.
- Collections.synchronizedList () - Elección 2 realizada por expertos. Esto aún no está completo, debido a las operaciones de varios pasos que deben ser atómicas (get / modify / set o iteration).
- Nuevas clases de java.util.concurrent: a menudo tienen algoritmos más eficientes que la opción 3. Se aplican advertencias similares sobre las operaciones de varios pasos, pero a menudo se proporcionan herramientas para ayudarlo.
siempre iría primero al paquete java.util.concurrent ( http://java.sun.com/javase/6/docs/api/java/util/concurrent/package-summary.html ) y vería si hay alguna colección adecuada. la clase java.util.concurrent.CopyOnWriteArrayList es buena si realiza pocos cambios en la lista pero más iteraciones.
Además, no creo que Vector and Collections.synchronizedList prevendrá ConcurrentModificationException.
Si no encuentra una colección adecuada, entonces tendría que hacer su propia sincronización, y si no desea mantener un bloqueo al iterar, puede considerar hacer una copia e iterar la copia.
Vector y la lista devuelta por Collections.synchronizedList () son moralmente la misma cosa. Consideraría que Vector se ha depreciado efectivamente (pero no en realidad) y siempre prefiero una Lista sincronizada. La única excepción serían las antiguas API (especialmente las que están en el JDK) que requieren un Vector.
Usar una ArrayList desnuda y sincronizar de forma independiente te brinda la oportunidad de sintonizar tu sincronización con mayor precisión (ya sea al incluir acciones adicionales en el bloque mutuamente excluyente o al juntar múltiples llamadas a la Lista en una acción atómica). El inconveniente es que es posible escribir código que accede a la ArrayList desnuda fuera de sincronización, que está rota.
Otra opción que quizás desee considerar es una CopyOnWriteArrayList, que le dará seguridad de subprocesos como Vector y ArrayList sincronizado, pero también iteradores que no arrojarán ConcurrentModificationException ya que están trabajando en una instantánea no viva de los datos.
Puede encontrar interesantes algunos de estos blogs recientes sobre estos temas: