java - example - AtomicInteger lazySet vs. set
atomicinteger java (6)
¿Cuál es la diferencia entre los métodos lazySet
y set
de AtomicInteger
? La documentation no tiene mucho que decir sobre lazySet
:
Eventualmente se establece en el valor dado.
Parece que el valor almacenado no se establecerá inmediatamente en el valor deseado, sino que se programará para establecerse en algún momento en el futuro. Pero, ¿cuál es el uso práctico de este método? ¿Algún ejemplo?
Aquí está mi entendimiento, corrígeme si estoy equivocado: puedes pensar en lazySet () como "semi" volátil: básicamente es una variable no volátil en términos de lectura por otros hilos, es decir, el valor establecido por lazySet puede no ser visible a otros hilos. Pero se vuelve volátil cuando se produce otra operación de escritura (puede ser de otros hilos). El único impacto de lazySet que puedo imaginar es compareAndGet. Entonces, si usa lazySet (), get () de otros subprocesos puede obtener el valor anterior, pero compareAndGet () siempre tendrá el nuevo valor ya que es una operación de escritura.
Citado directamente del error 6275329 :
Como probablemente sea el último pequeño seguimiento de JSR166 para Mustang, agregamos un método "lazySet" a las clases Atomic (AtomicInteger, AtomicReference, etc.). Este es un método de nicho que a veces es útil cuando se ajusta el código usando estructuras de datos sin bloqueo. La semántica es que se garantiza que la escritura no se reordene con ninguna escritura previa, pero puede reordenarse con operaciones posteriores (o de manera equivalente, puede no ser visible para otros hilos) hasta que ocurra alguna otra acción volátil de escritura o sincronización).
El principal caso de uso es para anular campos de nodos en estructuras de datos no bloqueadas únicamente con el fin de evitar la retención de basura a largo plazo; se aplica cuando es inofensivo si otros subprocesos ven valores no nulos por un tiempo, pero le gustaría asegurarse de que las estructuras sean eventualmente GCables. En tales casos, puede obtener un mejor rendimiento al evitar los costos de la escritura volátil nula. Hay algunos otros casos de uso en esta línea para los atómicos no basados en referencias, por lo que el método es compatible con todas las clases de AtomicX.
Para las personas a las que les gusta pensar en estas operaciones en términos de barreras a nivel de máquina en multiprocesadores comunes, lazySet proporciona una barrera preventa store-store (que no es operativa o es muy barata en las plataformas actuales), pero no tiene barreras de almacenamiento (que suele ser la parte cara de una escritura volátil).
Del resumen del paquete concurrente-atómico
LazySet tiene los efectos de memoria de escribir (asignar) una variable volátil, excepto que permite reordenamientos con acciones de memoria posteriores (pero no anteriores) que no imponen restricciones de reordenamiento con escrituras ordinarias no volátiles. Entre otros contextos de uso, lazySet puede aplicarse al anular, por el bien de la recolección de basura, una referencia a la que nunca se accede de nuevo.
Si tienes curiosidad acerca de lazySet, entonces debes también otras explicaciones
Los efectos de memoria para los accesos y actualizaciones de atomics generalmente siguen las reglas para volátiles, como se establece en la sección 17.4 de The Java ™ Language Specification.
get tiene los efectos de memoria de leer una variable volátil.
set tiene los efectos de memoria de escribir (asignar) una variable volátil.
LazySet tiene los efectos de memoria de escribir (asignar) una variable volátil, excepto que permite reordenamientos con acciones de memoria posteriores (pero no anteriores) que no imponen restricciones de reordenamiento con escrituras ordinarias no volátiles. Entre otros contextos de uso, lazySet puede aplicarse al anular, por el bien de la recolección de basura, una referencia a la que nunca se accede de nuevo.
weakCompareAndSet lee atómicamente y escribe condicionalmente una variable, pero no crea ninguna orden de suceso anterior, por lo que no ofrece garantías con respecto a las lecturas y escrituras anteriores o posteriores de cualquier variable que no sea el objetivo de weakCompareAndSet. compareAndSet y todas las demás operaciones de lectura y actualización, como getAndIncrement, tienen los efectos de memoria de lectura y escritura de variables volátiles.
LazySet se puede usar para comunicación entre hilos, porque xchg es atómico, en cuanto a visibilidad, cuando el proceso de hilos del escritor modifica una ubicación de línea de caché, el procesador del hilo del lector lo verá en la siguiente lectura, porque el protocolo de coherencia de caché de intel cpu garantizará LazySet funciona, pero la línea de caché se actualizará en la próxima lectura, una vez más, la CPU tiene que ser lo suficientemente moderna.
http://sc.tamu.edu/systems/eos/nehalem.pdf Para Nehalem, que es una plataforma multiprocesador, los procesadores tienen la capacidad de "husmear" (escuchar a hurtadillas) el bus de direcciones para los accesos de otros procesadores a la memoria del sistema y a sus cachés internos. Usan esta habilidad de espionaje para mantener sus cachés internos consistentes tanto con la memoria del sistema como con las cachés en otros procesadores interconectados. Si un intruso detecta que otro procesador intenta escribir en una ubicación de memoria que actualmente tiene en caché en estado Compartido, el procesador invalidará su bloque de caché obligándolo a realizar un relleno de línea de caché la próxima vez que acceda a la misma ubicación de memoria .
oracle hotspot jdk para la arquitectura de la CPU x86->
lazySet == unsafe.putOrderedLong == xchg rw (instrucción asm que sirve como una barrera suave que cuesta 20 ciclos en nehelem intel cpu)
en x86 (x86_64), dicha barrera es mucho más barata en cuanto a rendimiento que volátil o AtomicLong getAndAdd,
En un escenario de un productor, un cliente en cola, la barrera blanda xchg puede forzar la línea de códigos antes de que lazySet (secuencia + 1) suceda antes de cualquier hilo de consumidor que consumirá (trabajará) los nuevos datos, por supuesto El hilo del consumidor deberá verificar atómicamente que la secuencia del productor se incrementó exactamente en uno utilizando un conjunto de parámetros y comparar (secuencia, secuencia + 1).
Seguí el código fuente de Hotspot para encontrar la asignación exacta del código lazySet a cpp: http://hg.openjdk.java.net/jdk7/jdk7/hotspot/file/9b0ca45cd756/src/share/vm/prims/unsafe.cpp Unsafe_setOrderedLong -> definición de SET_FIELD_VOLATILE -> OrderAccess: release_store_fence. Para x86_64, OrderAccess: release_store_fence se define como el uso de la instrucción xchg.
Puede ver cómo se define exactamente en jdk7 (doug lea está trabajando en algunas cosas nuevas para JDK 8): http://hg.openjdk.java.net/jdk7/jdk7/hotspot/file/4fc084dac61e/src/os_cpu/linux_x86/vm/orderAccess_linux_x86.inline.hpp
también puede usar el hdis para desensamblar el ensamblaje del código de lazySet en acción.
Hay otra pregunta relacionada: ¿necesitamos mfence cuando usamos xchg?
Re: intento de atontarlo -
Puede pensar en esto como una forma de tratar un campo volátil como si no fuera volátil para una operación de tienda particular (por ej .: ref = null;).
Eso no es totalmente exacto, pero debería ser suficiente para poder tomar una decisión entre "OK, realmente no me importa" y "Hmm, déjame pensar en eso por un tiempo".
Se puede encontrar una discusión más amplia sobre los orígenes y la utilidad de lazySet y el subyacente putOrdered aquí: http://psy-lob-saw.blogspot.co.uk/2012/12/atomiclazyset-is-performance-win-for.html
Para resumir: lazySet es una escritura volátil débil en el sentido de que actúa como una tienda-tienda y no como una valla de almacenamiento. Esto se reduce a lazySet siendo JIT compilado a una instrucción MOV que no puede ser reordenada por el compilador en lugar de la instrucción significativamente más cara utilizada para un conjunto volátil.
Al leer el valor siempre termina haciendo una lectura volátil (con un Atomic * .get () en cualquier caso).
LazySet ofrece a un único escritor un mecanismo de escritura volátil consistente, es decir, es perfectamente legítimo que un escritor use lazySet para incrementar un contador, múltiples hilos que incrementen el mismo contador tendrán que resolver las escrituras de la competencia usando CAS, que es exactamente lo que sucede bajo las portadas de Atomic * para incAndGet.