2010-04-21 9 views
8

Estoy viendo este fragmento de código. Este constructor delega en el método nativo "System.arraycopy"Java - Seguridad de subprocesos de los constructores de ArrayList

¿Es seguro el subproceso? Y con eso quiero decir ¿puede lanzar alguna vez una ConcurrentModificationException?

public Collection<Object> getConnections(Collection<Object> someCollection) { 
    return new ArrayList<Object>(someCollection); 
} 

lo hace ninguna diferencia si la colección se está copiando es threadsafe por ejemplo, un CopyOnWriteArrayList?

public Collection<Object> getConnections(CopyOnWriteArrayList<Object> someCollection) { 
    return new ArrayList<Object>(someCollection); 
} 

Editar: Soy consciente de que threadsafe = ConcurrentModificationException!. Estoy tratando de tomar una instantánea de los datos en un punto en el tiempo. Por lo tanto, si otro subproceso escribe en alguna colección a mitad de camino a través de la copia, no me importa si el resultado tiene el nuevo objeto o no. Sólo que no quiere que se lance un ConcurrentModificationException o peores

Respuesta

4

Su pregunta es si puede obtener de forma segura una instantánea de una colección que podría estar siendo modificada concurrentemente por otro hilo usando new ArrayList<Foo>(thatCollection). La respuesta es: siempre que thatCollection sea seguro para subprocesos, sí. Entonces, si es un CopyOnWriteArrayList, synchronizedList o Vector, si no es seguro para subprocesos, por ejemplo si es otro ArrayList, no está bien. (¿Qué pasará podría ser peor que un ConcurrentModificationException.)

La razón es que el constructor ArrayList sólo hace una sola llamada atómica a la otra colección - a su método toArray. Por lo tanto, esencialmente disfruta de las garantías de seguridad que el método tiene. No siempre se implementó así, pero ahora es solo por esta razón. Hacemos lo mismo en Guava con ImmutableList.copyOf.

7

delegados este constructor para el método nativo "System.arraycopy"

En realidad, se llama en toArray()someCollection. Eso eventualmente llamará al System.arraycopy si someCollection es un ArrayList. Para otros tipos de colección, la matriz se creará de otras maneras.

¿Es seguro?

Y con esto quiero decir que nunca puede lanzar una ConcurrentModificationException?

Si se trata de un ArrayList no va a tirar ConcurrentModificationException ... pero eso no significa que sea seguro para subprocesos !!

Por ejemplo, si un subproceso diferente llama set(obj, pos) en someCollection mientras que el hilo está llamando a este constructor, entonces el contenido de su recién creada ArrayList son impredecibles.

1

ConcurrentModificationException no es el único signo de que algo no es seguro para subprocesos. Si, por ejemplo, en el método n. ° 1 someCollection también es ArrayList, nunca tendrá una ConcurrentModificationException (vea el código). Sin embargo, la integridad de los datos está en riesgo: si la ArrayList de origen se altera durante su copia, solo algunos de los cambios pueden reflejarse en la copia (¡y no necesariamente los cambios más antiguos!).

En otras palabras, no se garantiza la atomicidad (a menos que la fuente esté específicamente diseñada para ello, como con CopyOnWriteArrayList).

EDITAR: en realidad, suponiendo que no sincroniza sus hilos correctamente, copiar una matriz con una cuerda mientras que otra cuerda actualiza las referencias es problemático en el Modelo de memoria Java y, teóricamente, puede dar lugar a un comportamiento impredecible.

+0

Eso no es un problema teórico: el comportamiento impredecible * ocurrirá *. El único punto de incertidumbre es * con qué frecuencia * ocurrirá. –

+0

@Stephen: tienes razón, pero reproducir errores de escritura fuera de orden es extremadamente difícil. Solo encontré un ejemplo en el siguiente artículo: http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html –

+0

¡Difícil de reproducir! = Teórico. Además, algunos ejemplos se describen en páginas web! = Teórico. :-) –

5

Thread safe y ConcurrentModificationException son conceptos diferentes. Un objeto seguro para subprocesos es aquel en el que varios subprocesos pueden llamar a sus métodos al mismo tiempo, y se garantiza que los datos contenidos en el objeto no se corromperán (ejemplo: http://thejavacodemonkey.blogspot.com/2007/08/making-your-java-class-thread-safe.html).Una excepción ConcurrentModificationException ocurre cuando, por ejemplo, está en el medio de una iteración a través de una colección, y la colección cambia. El cambio puede provenir de un hilo diferente o del mismo hilo.

en su constructor, si otro hilo cambia el someCollection mientras que su constructor se copia, que podría dar lugar ya sea en un comportamiento indefinido (es decir, la corrupción de datos en su nueva colección, debido a que las colecciones no es seguro para subprocesos), o una ConcurrentModificationException (si la colección detecta la modificación concurrente, pero esto no está garantizado, porque no es seguro para subprocesos ... :-)

Si su constructor va a tomar un Collection<Object>, deberá asegurarse de que otros subprocesos no modifique la colección hasta después de que su constructor regrese.

Por otro lado, un hilo de seguridad es CopyOnWriteArrayList y garantías de no tirar ConcurrentModificationException, por lo que debe ser seguro hacerlo de esta manera, sin necesidad de escribir código de sincronización adicional.

Cuestiones relacionadas