2010-06-01 8 views
48

He echado un vistazo a OpenJDK source code de CopyOnWriteArrayList y parece que todas las operaciones de escritura están protegidas por el mismo bloqueo y las operaciones de lectura no están protegidas en absoluto. Según tengo entendido, en JMM todos los accesos a una variable (tanto de lectura como de escritura) deben estar protegidos por bloqueo o pueden producirse nuevos efectos de reordenación.¿Cómo puede CopyOnWriteArrayList ser seguro para subprocesos?

Por ejemplo, set(int, E) método contiene estas líneas (menores de bloqueo):

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

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

En mi comprensión de JMM, esto significa que get puede observar la matriz en un estado incoherente si las instrucciones 1-4 se reordenan como 1-2 (nuevo) -4-2 (copyOf) -3.

¿Entiendo JMM incorrectamente o hay alguna otra explicación sobre por qué CopyOnWriteArrayList es thread-safe?

Respuesta

61

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

Lo importante es que la actualización de la matriz es una operación atómica y, por lo tanto, las lecturas siempre verán la matriz en un estado consistente.

La ventaja de solo sacar un candado para las operaciones de escritura es un rendimiento mejorado para las lecturas: Esto se debe a que las operaciones de escritura para un CopyOnWriteArrayList pueden ser muy lentas ya que implican copiar toda la lista.

+0

Gracias. Me he perdido el hecho de que la matriz es 'volátil '. – Fixpoint

+15

Un detalle importante es que volátil solo se aplica a la referencia de matriz en sí, no al contenido de la matriz. Sin embargo, dado que todos los cambios en la matriz se realizan ** antes ** de que se publique su referencia, las garantías volátiles se extienden al contenido de la matriz. – assylias

16

Obtener la referencia de matriz es una operación atómica. Entonces, los lectores verán la matriz anterior o la nueva matriz, de cualquier manera el estado es consistente. (set(int,E) calcula los nuevos contenido de la matriz antes de la referencia, por lo que la matriz es consistente cuando se hace la asignment.) Referencia

La matriz en sí está marcado como volatile para que los lectores no tienen que usar una cerradura para ver los cambios en la matriz referenciada. (EDITAR: Además, volatile garantiza que la asignación no se reordena, lo que daría lugar a que la asignación se realice cuando la matriz posiblemente esté en un estado incoherente.)

El bloqueo de escritura es necesario para evitar modificaciones simultáneas, que puede hacer que la matriz contenga datos inconsistentes o se pierdan los cambios.

+1

Esto no es 100% exacto. La atomicidad de establecer la referencia no es suficiente para garantizar la coherencia, y las reglas del Modelo de memoria de Java resuelven este problema. Las escrituras desordenadas y el reordenamiento de las instrucciones pueden ocurrir, y luego un hilo puede recibir una referencia que apunta a un objeto inconsistente. Esto también ocurre con el patrón de doble verificación de bloqueo (consulte http://www.ibm.com/developerworks/java/library/j-dcl.html) –

+3

No es lo mismo. leer/escribir en un elemento volátil se compone de lo que el JMM denomina una "acción sincronizada" y define una barrera sobre lo que se puede reordenar. Consulte http://java.sun.com/docs/books/jls/third_edition/html/memory.html – mdma

+1

@Eyal Schneider: Bienvenido al 2004 (consulte http://www.ibm.com/developerworks/library/j- jtp03304 /). Lea la sección titulada "Nuevas garantías para volátiles" –

Cuestiones relacionadas