Respuestas:
La respuesta corta es: No.
De la java.util.concurrent.atomic
documentación del paquete. Citar:
Los efectos de memoria para accesos y actualizaciones de atómicos generalmente siguen las reglas para volátiles:
get
tiene los efectos de memoria de leer unavolatile
variable.set
tiene los efectos de memoria de escribir (asignar) unavolatile
variable.
Por cierto, esa documentación es muy buena y todo está explicado.
AtomicReference::lazySet
es una nueva operación (Java 6+) introducida que tiene una semántica inalcanzable a través de volatile
variables. Vea esta publicación para más información.
Hay varias diferencias y compensaciones:
El uso de un AtomicReference
get / set tiene la misma semántica JMM que un campo volátil (como indica el javadoc), pero AtomicReference
es un contenedor alrededor de una referencia, por lo que cualquier acceso al campo implica una búsqueda de puntero adicional .
La huella de memoria se multiplica (suponiendo un entorno OOP comprimido, lo cual es cierto para la mayoría de las máquinas virtuales):
AtomicReference
= 4b + 16b (encabezado de objeto 12b + campo de referencia 4b)AtomicReference
ofrece una API más rica que una referencia volátil. Puede recuperar la API para la referencia volátil utilizando un AtomicFieldUpdater
o con Java 9 a VarHandle
. También puedes alcanzarlo directamente sun.misc.Unsafe
si te gusta correr con unas tijeras. AtomicReference
se implementa usando Unsafe
.
Entonces, ¿cuándo es bueno elegir uno sobre el otro?
AtomicReference
/ AtomicFieldUpdater
/ Unsafe
donde tiende a pagar en legibilidad y riesgo para su ganancia de rendimiento. Si esta no es un área sensible, simplemente busque AtomicReference
. Los escritores de bibliotecas suelen utilizar una combinación de estos métodos según los JDK específicos, las restricciones API esperadas, las restricciones de memoria, etc.El código fuente JDK es una de las mejores formas de responder a confusiones como esta. Si observa el código en AtomicReference, utiliza una variable de volatilidad para el almacenamiento de objetos.
private volatile V value;
Entonces, obviamente, si va a usar get () y set () en AtomicReference, es como usar una variable volátil. Pero como comentaron otros lectores, AtomicReference proporciona semántica CAS adicional. Por lo tanto, primero decida si desea la semántica de CAS o no, y si solo lo desea, use AtomicReference.
AtomicReference
proporciona funcionalidad adicional que una variable volátil simple no proporciona. Como haya leído el Javadoc API, lo sabrá, pero también proporciona un bloqueo que puede ser útil para algunas operaciones.
Sin embargo, a menos que necesite esta funcionalidad adicional, le sugiero que use un volatile
campo simple .
volatile
campo se puede usar como cualquier campo normal, mientras que para acceder al valor en un se AtomicReference
requiere revisar get
y set
métodos.
A veces, incluso si solo usa gets y sets, AtomicReference puede ser una buena opción:
Ejemplo con volátil:
private volatile Status status;
...
public setNewStatus(Status newStatus){
status = newStatus;
}
public void doSomethingConditionally() {
if(status.isOk()){
System.out.println("Status is ok: " + status); // here status might not be OK anymore because in the meantime some called setNewStatus(). setNewStatus should be synchronized
}
}
La implementación con AtomicReference le proporcionaría una sincronización de copia en escritura de forma gratuita.
private AtomicReference<Status> statusWrapper;
...
public void doSomethingConditionally() {
Status status = statusWrapper.get();
if(status.isOk()){
System.out.println("Status is ok: " + status); // here even if in the meantime some called setNewStatus() we're still referring to the old one
}
}
Se podría decir que aún podría tener una copia adecuada si sustituye:
Status status = statusWrapper.get();
con:
Status statusCopy = status;
Sin embargo, es más probable que alguien elimine el segundo accidentalmente en el futuro durante la "limpieza del código".