¿Cuándo usamos AtomicReference
?
¿Es necesario crear objetos en todos los programas multiproceso?
Proporcione un ejemplo simple donde se debe usar AtomicReference.
fuente
¿Cuándo usamos AtomicReference
?
¿Es necesario crear objetos en todos los programas multiproceso?
Proporcione un ejemplo simple donde se debe usar AtomicReference.
La referencia atómica debe usarse en un entorno en el que necesite realizar operaciones atómicas simples (es decir , seguras para subprocesos , no triviales) en una referencia, para la cual la sincronización basada en monitor no es adecuada. Suponga que desea verificar si un campo específico solo si el estado del objeto permanece como lo comprobó por última vez:
AtomicReference<Object> cache = new AtomicReference<Object>();
Object cachedValue = new Object();
cache.set(cachedValue);
//... time passes ...
Object cachedValueToUpdate = cache.get();
//... do some work to transform cachedValueToUpdate into a new version
Object newValue = someFunctionOfOld(cachedValueToUpdate);
boolean success = cache.compareAndSet(cachedValue,cachedValueToUpdate);
Debido a la semántica de referencia atómica, puede hacerlo incluso si el cache
objeto se comparte entre subprocesos, sin usar synchronized
. En general, es mejor usar sincronizadores o el java.util.concurrent
marco en lugar de estar desnudo a Atomic*
menos que sepa lo que está haciendo.
Dos excelentes referencias de árbol muerto que le presentarán este tema:
Tenga en cuenta que (no sé si esto siempre ha sido cierto) la asignación de referencia (es decir =
) es atómica en sí misma (actualizar tipos primitivos de 64 bits como long
o double
no puede ser atómica; pero actualizar una referencia siempre es atómica, incluso si es de 64 bits ) sin usar explícitamente un Atomic*
.
Consulte la Especificación del lenguaje Java 3ed, Sección 17.7 .
AtomicReference
, debe marcar la variablevolatile
porque, si bien el tiempo de ejecución garantiza que la asignación de referencia es atómica, el compilador puede realizar optimizaciones bajo el supuesto de que la variable no estaba siendo modificada por otros subprocesos.AtomicReference
"; si lo está utilizando, mi consejo sería ir en la dirección opuesta y marcarlofinal
para que el compilador pueda optimizarlo en consecuencia.Una referencia atómica es ideal para usar cuando necesita compartir y cambiar el estado de un objeto inmutable entre múltiples hilos. Esa es una declaración súper densa, así que la desglosaré un poco.
Primero, un objeto inmutable es un objeto que efectivamente no cambia después de la construcción. Con frecuencia, los métodos de un objeto inmutable devuelven nuevas instancias de esa misma clase. Algunos ejemplos incluyen las clases de contenedor de Long y Double, así como String, solo por nombrar algunos. (De acuerdo con la concurrencia de programación en la JVM, los objetos inmutables son una parte crítica de la concurrencia moderna).
A continuación, por qué AtomicReference es mejor que un objeto volátil para compartir ese valor compartido. Un ejemplo de código simple mostrará la diferencia.
Cada vez que desee modificar la cadena a la que hace referencia ese campo volátil en función de su valor actual, primero debe obtener un bloqueo en ese objeto. Esto evita que algún otro hilo entre mientras tanto y cambie el valor en el medio de la nueva concatenación de cadenas. Luego, cuando se reanuda su hilo, golpea el trabajo del otro hilo. Pero, sinceramente, ese código funcionará, se ve limpio y haría feliz a la mayoría de las personas.
Pequeño problema. Es lento. Especialmente si hay mucha contención de ese Objeto de bloqueo. Esto se debe a que la mayoría de los bloqueos requieren una llamada al sistema operativo, y su subproceso se bloqueará y se cambiará el contexto de la CPU para dar paso a otros procesos.
La otra opción es usar una referencia atómica.
Ahora, ¿por qué es esto mejor? Honestamente, ese código es un poco menos limpio que antes. Pero hay algo realmente importante que sucede debajo del capó en AtomicRefrence, y es comparar e intercambiar. Es una sola instrucción de CPU, no una llamada del sistema operativo, lo que hace que el cambio ocurra. Esa es una sola instrucción en la CPU. Y debido a que no hay bloqueos, no hay cambio de contexto en el caso de que el bloqueo se ejerza, lo que ahorra aún más tiempo.
El problema es que, para AtomicReferences, esto no utiliza una llamada .equals (), sino una comparación == para el valor esperado. Así que asegúrese de que lo esperado sea el objeto real devuelto por get in the loop.
fuente
worked
que seguir para obtener la misma semántica.Aquí hay un caso de uso para AtomicReference:
Considere esta clase que actúa como un rango de números y utiliza variables AtmomicInteger individuales para mantener los límites numéricos inferior y superior.
Tanto setLower como setUpper son secuencias de verificar y luego actuar, pero no usan suficiente bloqueo para hacerlas atómicas. Si el rango de números se mantiene (0, 10), y un subproceso llama a setLower (5) mientras que otro subproceso llama a setUpper (4), con un tiempo desafortunado, ambos pasarán las verificaciones en los setters y se aplicarán ambas modificaciones. El resultado es que el rango ahora contiene (5, 4) un estado no válido. Entonces, mientras que los AtomicIntegers subyacentes son seguros para subprocesos, la clase compuesta no lo es. Esto se puede solucionar mediante el uso de AtomicReference en lugar de utilizar AtomicIntegers individuales para los límites superior e inferior.
fuente
Puede usar AtomicReference al aplicar bloqueos optimistas. Tiene un objeto compartido y desea cambiarlo desde más de 1 hilo.
Como otro hilo podría haberlo modificado y / o puede modificar entre estos 2 pasos. Necesitas hacerlo en una operación atómica. aquí es donde AtomicReference puede ayudar
fuente
Aquí hay un caso de uso muy simple y no tiene nada que ver con la seguridad del hilo.
Para compartir un objeto entre invocaciones lambda,
AtomicReference
es una opción :No estoy diciendo que este sea un buen diseño ni nada (es solo un ejemplo trivial), pero si tiene el caso en el que necesita compartir un objeto entre invocaciones lambda,
AtomicReference
es una opción.De hecho, puede usar cualquier objeto que contenga una referencia, incluso una Colección que solo tenga un elemento. Sin embargo, la referencia atómica es perfecta.
fuente
No hablaré mucho Ya mis respetados compañeros amigos han dado su valiosa aportación. El código de ejecución completo al final de este blog debería eliminar cualquier confusión. Se trata de un pequeño programa de reserva de asientos de cine en un escenario de subprocesos múltiples.
Algunos hechos elementales importantes son los siguientes. 1> Diferentes subprocesos solo pueden competir, por ejemplo, con variables miembro estáticas en el espacio de almacenamiento dinámico. 2> La lectura o escritura volátil es completamente atómica y serializada / ocurre antes y solo se realiza desde la memoria. Al decir esto quiero decir que cualquier lectura seguirá a la escritura anterior en la memoria. Y cualquier escritura seguirá la lectura anterior de la memoria. Por lo tanto, cualquier hilo que trabaje con un volátil siempre verá el valor más actualizado. AtomicReference utiliza esta propiedad de volátil.
Los siguientes son algunos de los códigos fuente de AtomicReference. AtomicReference se refiere a una referencia de objeto. Esta referencia es una variable miembro volátil en la instancia de AtomicReference como se muestra a continuación.
get () simplemente devuelve el último valor de la variable (como lo hacen los volátiles de una manera "ocurre antes").
El siguiente es el método más importante de referencia atómica.
El método compareAndSet (esperar, actualizar) llama al método compareAndSwapObject () de la clase insegura de Java. Este método de llamada de inseguro invoca la llamada nativa, que invoca una sola instrucción para el procesador. "esperar" y "actualizar" cada referencia a un objeto.
Si y solo si el "valor" de la variable de miembro de instancia AtomicReference se refiere al mismo objeto al que se hace referencia por "esperar", ahora se asigna "actualización" a esta variable de instancia y se devuelve "verdadero". O bien, se devuelve falso. Todo se hace atómicamente. Ningún otro hilo puede interceptar en el medio. Como se trata de una operación de procesador único (magia de la arquitectura moderna de la computadora), a menudo es más rápido que usar un bloque sincronizado. Pero recuerde que cuando varias variables necesitan actualizarse atómicamente, AtomicReference no ayudará.
Me gustaría agregar un código de ejecución completo, que se puede ejecutar en eclipse. Despejaría mucha confusión. Aquí 22 usuarios (hilos MyTh) están tratando de reservar 20 asientos. A continuación se muestra el fragmento de código seguido del código completo.
Fragmento de código donde 22 usuarios intentan reservar 20 asientos.
El siguiente es el código de ejecución completo.
fuente
AtomicReference es una forma flexible de actualizar el valor de la variable atómicamente sin el uso de sincronización.
AtomicReference
admite programación segura y sin hilos de bloqueo en variables individuales.Hay varias formas de lograr la seguridad de subprocesos con API concurrente de alto nivel . Las variables atómicas son una de las múltiples opciones.
Lock
Los objetos admiten modismos de bloqueo que simplifican muchas aplicaciones concurrentes.Executors
definir una API de alto nivel para lanzar y administrar hilos. Las implementaciones de ejecutor proporcionadas por java.util.concurrent proporcionan una gestión de grupo de subprocesos adecuada para aplicaciones a gran escala.Las colecciones simultáneas facilitan la gestión de grandes colecciones de datos y pueden reducir en gran medida la necesidad de sincronización.
Las variables atómicas tienen características que minimizan la sincronización y ayudan a evitar errores de consistencia de la memoria.
Código de muestra con
AtomicReference
:No tiene que usarlo
AtomicReference
en todos los programas de subprocesos múltiples.Si desea proteger una sola variable, use
AtomicReference
. Si desea proteger un bloque de código, use otras construcciones comoLock
/synchronized
etc.fuente
Otro ejemplo simple es hacer una modificación de hilo seguro en un objeto de sesión.
Fuente: http://www.ibm.com/developerworks/library/j-jtp09238/index.html
fuente