56 votos

¿Cómo puede CopyOnWriteArrayList ser un hilo seguro?

He echado un vistazo a El código fuente de OpenJDK de CopyOnWriteArrayList y parece que todas las operaciones de escritura están protegidas por el mismo candado y las operaciones de lectura no están protegidas en absoluto. Según tengo entendido, bajo JMM todos los accesos a una variable (tanto de lectura como de escritura) deberían estar protegidos por un candado o pueden producirse efectos de reordenación.

Por ejemplo, set(int, E) El método contiene estas líneas (bajo llave):

/* 1 */ int len = elements.length;
/* 2 */ Object[] newElements = Arrays.copyOf(elements, len);
/* 3 */ newElements[index] = element;
/* 4 */ setArray(newElements);

El get(int) El método, por otra parte, sólo hace return get(getArray(), index); .

En mi entendimiento de JMM, esto significa que get puede observar la matriz en un estado inconsistente si las declaraciones 1-4 se reordenan como 1-2(nuevo)-4-2(copiaOf)-3.

¿Entiendo JMM incorrectamente o hay alguna otra explicación de por qué CopyOnWriteArrayList es seguro para el hilo?

70voto

Adamski Puntos 29884

Si miras la referencia de la matriz subyacente verás que está marcada como volatile . Cuando se produce una operación de escritura (como en el extracto anterior) esta volatile referencia sólo se actualiza en la declaración final a través de setArray . Hasta este punto cualquier operación de lectura devolverá elementos de la copia antigua de la matriz.

El punto importante es que el La actualización de la matriz es una operación atómica y por lo tanto las lecturas siempre verán el Array en un estado consistente.

La ventaja de sacar sólo un candado para las operaciones de escritura es la mejora del rendimiento de las lecturas: Esto se debe a que las operaciones de escritura para un CopyOnWriteArrayList pueden ser potencialmente muy lentos ya que implican copiar toda la lista.

19voto

mdma Puntos 33973

Obtener la referencia de la matriz es una operación atómica. Así que los lectores verán el viejo o el nuevo Conjunto, de cualquier manera el estado es consistente. ( set(int,E) calcula el contenido del nuevo Array antes de establecer la referencia, para que el Array sea consistente cuando se haga la asignación).

La referencia de la matriz en sí misma está marcada como volatile para que los lectores no necesiten usar un candado para ver los cambios en el Array referido. (EDITORIAL: También, volatile garantiza que la cesión no se reordene, lo que llevaría a que la cesión se haga cuando el Array esté posiblemente en un estado inconsistente).

El bloqueo de escritura es necesario para evitar la modificación simultánea, que puede dar lugar a que la matriz contenga datos incoherentes o que se pierdan los cambios.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X