2010-11-23 23 views
8

Estaba buscando el código de colecciones simultáneas de Java y veo que simplemente envuelven colecciones simples con bloqueo de algún bloqueo al principio de la operación y desbloqueándolo al final.Por qué las colecciones concurrentes de Java son realmente seguras para subprocesos

¿Qué hay de volatile? Si la colección de back-end no es volátil, otros subprocesos podrían perder los cambios y, por lo tanto, el almacenamiento de la tarea de ejecución de subprocesos se rompe un tanto. Sé que synchronized puede resolver este problema, pero solo usan bloqueos sin más sincronización.

¿Eso es un problema o me falta algo?


Actualización:

Después de una ligera discusión quiero reformular la pregunta un poco.

Quiero usar una colección de Java en un entorno de múltiples hilos. (Por ejemplo, actualmente estoy hablando de PriorityBlockingQueue)

Quiero estar seguro de que los cambios que hace un hilo en la colección (push/pop) son inmediatamente visibles para los demás.

Es bueno que las colecciones simultáneas de Java eviten que me sumerja en problemas para mantener estable el estado interno de la colección cuando el número de subprocesos lo actualiza, pero Quiero asegurarme de que los datos sean visibles para todos los hilos.

La pregunta es: ¿estoy seguro de que las colecciones simultáneas de Java no proporcionan esta característica de la caja? Y si lo hago, ¿qué técnicas adicionales (costo minimalista) debo usar para proporcionar la visibilidad deseada?

Gracias.

+0

Es importante tener en cuenta que 'sincronizado' en Java no es solo un 'mutex' o un bloqueo. Es un contrato sobre lo que sucede, antes de las relaciones y la coherencia de la vista. Un bloque sincronizado tiene un alcance dinámico a medida que desciende las pilas de llamadas junto con la ejecución (frente a ser simplemente léxico en el alcance). Ver http://stackoverflow.com/questions/3214938/java-volatile-modifier-and-synchronized-blocks (He visto algunas preguntas SO realmente buenas que muestran el JLS en detalle, pero no puedo encontrarlas en el momento: - /) Tal vez vea: http://java.sun.com/docs/books/jls/second_edition/html/memory.doc.html –

+0

No quiero un mutex, solo quiero que todos los cambios se vería a todos los hilos. Por lo tanto, quiero evitar el uso de mecanismos de sincronización innecesarios. – jutky

+0

Es importante tener en cuenta que 'sincronizado' en Java * no es solo * un 'mutex' o un bloqueo .... hay otros métodos de sincronización, pero se toman un poco más de reflexión. 1) Un algoritmo "libre de bloqueo" como ConcurrentLinkedQueue 2) volátil (no crea secciones atómicas) 3) una de las clases Atomic * (soporte CAS e incremento atómico, etc.) ... de todos modos. –

Respuesta

11

De BlockingQueue 's Javadoc:

efectos de coherencia de la memoria: Al igual que con otras colecciones concurrentes, las acciones en un hilo antes de la colocación de un objeto en un BlockingQueuesuceder antes acciones posterior a la de acceso o eliminación de ese elemento del BlockingQueue en otro hilo.

PriorityBlockingQueue proporciona este comportamiento por medio de un ReentrantLock que (siendo una implementación de Lock):

... proporcionar [s] la misma semántica de sincronización memoria como proporcionado por el monitor incorporado cerradura, según lo descrito en el JLS ...

+0

Muchas gracias. Hiciste mi día :) – jutky

5

EDIT: desde que se aclaró la pregunta original, esta respuesta ya no es relevante. La respuesta a continuación es relevante para el caso cuando se usan los métodos Collections.synchronized* para hacer que las colecciones que no son aptas para roscar sean seguras para el hilo.


Si sincroniza un bloque de código que también causará diferentes hilos para sincronizar el estado (posiblemente cambiado).

[...] Cualquiera de las soluciones requiere que la variable clkID se reconcilie con la memoria principal. El acceso a la variable clkID desde el método o bloque sincronizado no permite que el código se ejecute al mismo tiempo, pero sí garantiza que la variable clkID en la memoria principal se actualice adecuadamente. La memoria principal se actualiza cuando se obtiene el bloqueo del objeto antes de que se ejecute el código protegido, y luego cuando se libera el bloqueo después de que se ejecuta el código protegido. [...]

Fuente: Use Synchronized or Volatile when Accessing Shared Variables

+0

Entiendo eso, pero ¿no es el objetivo de las colecciones concurrentes evitar cualquier otra sincronización explícita? corrígeme si me equivoco. – jutky

+0

No estoy seguro de entender su pregunta. ¿Qué quiere decir con "cualquier otra sincronización explícita"? El objetivo de la envoltura de recolección sincronizada es sincronizar el acceso a la colección que, de otro modo, no sería segura para la rosca. –

+0

Como mencionó Bemrose, sería útil si declarara explícitamente qué fuente de clase está mirando. –

6

yo estaba buscando en el código de Java colecciones concurrentes y veo que simplemente envuelven colecciones sencillas con bloqueo algún bloqueo en el comienzo de la operación y desbloquearlo en el extremo .

¿Qué fuente estabas leyendo?

Esto es una generalización excesiva. Depende completamente de qué colección estás mirando. Por ejemplo, CopyOnWriteArrayList no hace nada por el estilo, pero crea una matriz completamente nueva cada vez que agrega o elimina elementos, lo que significa que cualquier Iterator o ListIterator abierto continuará ejecutándose con los datos anteriores; este efecto es intencional.

¿Qué hay de volátil? Si la colección de la parte posterior no es volátil, los cambios podrían pasar desapercibidos por otros hilos, y , por lo que el ahorro de hilo es algo roto. Sé que la lata sincronizada resuelve este problema, pero solo usan cerraduras sin ninguna sincronización adicional .

La mayoría de las colecciones concurrentes existen para garantizar que los Iterators continúen operando en la versión anterior en lugar de la nueva versión que tiene datos actualizados; algo que volatile no garantizaría. Este comportamiento también significa que los Iteradores no se dejarán en un estado incoherente y, por lo tanto, impedirán que se genere ConcurrentModificationException.

+0

Estaba viendo 'PriorityBlockingQueue'. – jutky

+0

Pero si quiero que varios hilos usen la misma colección, no puedo volver a implementar la colección de red troncal para que sea 'volátil '. Entonces, ¿estoy obligado a sincronizar cada acceso a la colección? – jutky

+1

@jutky No es equivalente. 'volátil' solo" vacía "el acceso a una * sola variable * (esto no se propaga a ningún dato del objeto real para un tipo de referencia). ** No introduce una sección atómica. **. Este código trivial no es seguro para subprocesos (supongamos que es una int volátil compartida): 'if (i> 10) {i = 0; ...} else {i ++; ...} ' –

7

sí, te estás perdiendo algo. la clase ReentrantLock proporciona las mismas garantías que las sincronizadas. Y ReentrantLock y sincronizados proporcionan las mismas garantías de memoria que los volátiles.

+0

Wow. Desearía que fuera así de simple. ¿Podría citar una fuente oficial que lo indique? – jutky

+0

Creo que estás equivocado. ¿Qué sucede si obtengo un bloqueo en una función y luego lo libero en otra función? ¿Se sincronizaría todo el código que se ejecuta mientras tanto? Eso no tiene sentido. – jutky

+3

"Todas las implementaciones' Lock' * deben * imponer la misma semántica de sincronización de memoria que la proporcionada por el bloqueo del monitor incorporado, como se describe en el JLS "- de http://download.oracle.com/javase/6/docs/ api/java/util/concurrent/locks/Lock.html – SimonJ

0

Aquí está el punto sabio responde a sus preguntas. 1. Todas las estructuras de datos de recopilación de Java en el paquete de concurrencia no envuelven las estructuras de datos de recopilación seguras que no son resistentes a subprocesos. 2. Hay formas de lograr seguridad en el hilo sin usar bloqueos o sincronización.Escribir estructuras de datos libres de bloqueo es más complicado y, por lo general, debe evitarse para el desarrollo general. Pero si hay uno disponible, debe usarlos en lugar de la versión de bloqueo correspondiente de la estructura de datos. 3. Bloquear la estructura de datos libre no utiliza bloqueos, todavía proporciona toda la propiedad de seguridad y vitalidad. 4. API de muestra es ConcurrentSkipListMap, que es un mapa probabilístico simultáneo sin bloqueo (probablemente la implementación más complicada en Java)

+0

1 - alguna vez lo hacen. 2 - no estoy de acuerdo, a veces hay situaciones en las que está obligado a usar la sincronización. 4 - ConcurrentSkipListMap de hecho una clase complicada, nunca la había usado antes. Echaré un vistazo de qué se trata. Gracias. – jutky

Cuestiones relacionadas