Actualmente estoy trabajando en una aplicación de subprocesos múltiples, y de vez en cuando recibo una excepción de modificación concurrente (aproximadamente una o dos veces por hora en promedio, pero que ocurre en intervalos aparentemente aleatorios).Excepción simultánea Excepción
La clase defectuosa es esencialmente un contenedor para un mapa, que se extiende a LinkedHashMap
(con accessOrder configurado en verdadero). La clase tiene algunos métodos:
synchronized set(SomeKey key, SomeValue val)
El método conjunto añade un par de claves/valor al mapa interno, y está protegido por la palabra clave sincronizada.
synchronized get(SomeKey key)
El método get devuelve el valor en función de la tecla de entrada.
rebuild()
El mapa interno se reconstruye de vez en cuando (~ cada 2 minutos, intervalos no coinciden con las excepciones). El método de reconstrucción esencialmente reconstruye los valores basados en sus claves. Como Rebuild() es bastante caro, no puse una palabra clave sincronizada en el método. En cambio, yo estoy haciendo:
public void rebuild(){
/* initialization stuff */
List<SomeKey> keysCopy = new ArrayList<SomeKey>();
synchronized (this) {
keysCopy.addAll(internalMap.keySet());
}
/*
do stuff with keysCopy, update a temporary map
*/
synchronized (this) {
internalMap.putAll(tempMap);
}
}
La excepción se produce en
keysCopy.addAll(internalMap.keySet());
Dentro del bloque sincronizado.
Las sugerencias son muy apreciadas. No dude en dirigirme a páginas/capítulos específicos en Java efectiva y/o Concurrencia en la práctica.
Actualización 1:
StackTrace Sanitized:
java.util.ConcurrentModificationException
at java.util.LinkedHashMap$LinkedHashIterator.nextEntry(LinkedHashMap.java:365)
at java.util.LinkedHashMap$KeyIterator.next(LinkedHashMap.java:376)
at java.util.AbstractCollection.toArray(AbstractCollection.java:126)
at java.util.ArrayList.addAll(ArrayList.java:473)
at a.b.c.etc.SomeWrapper.rebuild(SomeWraper.java:109)
at a.b.c.etc.SomeCaller.updateCache(SomeCaller.java:421)
...
Actualización 2:
Gracias a todos por las respuestas hasta el momento. Creo que el problema se encuentra dentro del LinkedHashMap y su atributo accessOrder, aunque no estoy del todo seguro (investigando).
Si accessOrder en un LinkedHashMap se establece en true, y acceder a su conjunto de claves a continuación, proceder a añadir el conjunto de claves a un LinkedList través addAll, realice una de estas acciones mutar el orden (es decir, cuentan para un "acceso")?
OT, pero ¿no estás en peligro de perder cualquier cambio en la clase en la región comentada entre los dos métodos sincronizados en Rebuild()? – DaveR
Sí, tienes toda la razón; pero de acuerdo con los documentos de diseño, es aceptable tener datos algo obsoletos :) – Cambium
Maldita sea, ojalá tuviera documentos de diseño así;) – DaveR