Booleano volátil vs AtomicBoolean
¿Qué hace AtomicBoolean que un booleano volátil no pueda lograr?
Utilizo campos volátiles cuando dicho campo SÓLO es ACTUALIZADO por su subproceso propietario y el valor solo lo leen otros subprocesos. Puede considerarlo como un escenario de publicación/suscripción donde hay muchos observadores pero solo un editor. Sin embargo, si esos observadores deben realizar alguna lógica basada en el valor del campo y luego retroceder un nuevo valor, entonces uso vars Atomic* o bloqueos o bloques sincronizados, lo que más me convenga. En muchos escenarios concurrentes, todo se reduce a obtener el valor, compararlo con otro y actualizarlo si es necesario, de ahí los métodos compareAndSet y getAndSet presentes en las clases Atomic*.
Consulte los JavaDocs del paquete java.util.concurrent.atomic para obtener una lista de clases Atomic y una excelente explicación de cómo funcionan (acabo de enterarme de que no tienen bloqueos, por lo que tienen una ventaja sobre los bloqueos o bloques sincronizados)
Son simplemente totalmente diferentes. Considere este ejemplo de un volatile
número entero:
volatile int i = 0;
void incIBy5() {
i += 5;
}
Si dos subprocesos llaman a la función al mismo tiempo, i
podrían ser 5 después, ya que el código compilado será algo similar a este (excepto que no se puede sincronizar int
):
void incIBy5() {
int temp;
synchronized(i) { temp = i }
synchronized(i) { i = temp + 5 }
}
Si una variable es volátil, cada acceso atómico a ella se sincroniza, pero no siempre es obvio qué se considera realmente un acceso atómico. Con un Atomic*
objeto, se garantiza que cada método sea "atómico".
Por lo tanto, si usa un AtomicInteger
y getAndAdd(int delta)
, puede estar seguro de que el resultado será 10
. De la misma manera, si dos subprocesos niegan una boolean
variable al mismo tiempo, con an AtomicBoolean
puede estar seguro de que tiene el valor original después, con a volatile boolean
no puede hacerlo.
Entonces, siempre que tenga más de un hilo modificando un campo, debe hacerlo atómico o usar sincronización explícita.
El propósito de volatile
es otro. Considere este ejemplo
volatile boolean stop = false;
void loop() {
while (!stop) { ... }
}
void stop() { stop = true; }
Si tiene un subproceso ejecutándose loop()
y otro subproceso llamando a stop()
, podría encontrarse con un bucle infinito si omite volatile
, ya que el primer subproceso podría almacenar en caché el valor de parada. Aquí, esto volatile
sirve como una pista para que el compilador sea un poco más cuidadoso con las optimizaciones.
compareAndSet
No puede hacerlo getAndSet
como operación atómica con booleano volátil (a menos, por supuesto, que lo sincronice).