¿Qué hace AtomicBoolean que un booleano volátil no puede lograr?
¿Qué hace AtomicBoolean que un booleano volátil no puede lograr?
Respuestas:
Son simplemente totalmente diferentes. Considere este ejemplo de un volatile
entero:
volatile int i = 0;
void incIBy5() {
i += 5;
}
Si dos hilos llaman a la función simultáneamente, i
puede ser 5 después, ya que el código compilado será algo similar a esto (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 está sincronizado, pero no siempre es obvio lo que realmente califica como acceso atómico. Con un Atomic*
objeto, se garantiza que cada método es "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 un AtomicBoolean
puede estar seguro de que tiene el valor original después, con un volatile boolean
, no puede.
Por lo tanto, 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 diferente. Considera 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 stop()
, podría omitir un bucle infinito si omite volatile
, ya que el primer subproceso podría almacenar en caché el valor de stop. Aquí, volatile
sirve como una pista para que el compilador sea un poco más cuidadoso con las optimizaciones.
volatile
. La pregunta es acerca volatile boolean
vs AtomicBoolean
.
volatile boolean
es suficiente. Si también hay muchos escritores, es posible que lo necesite AtomicBoolean
.
Utilizo campos volátiles cuando dicho campo SÓLO ES ACTUALIZADO por su hilo propietario y el valor solo lo leen otros hilos, puede pensarlo 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 voy con Atomic * vars o bloqueos o bloques sincronizados, lo que mejor me convenga. En muchos escenarios concurrentes, se reduce a obtener el valor, compararlo con otro y actualizar 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 atómicas 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)
boolean
var, deberíamos elegir volatile boolean
.
No puede hacerlo compareAndSet
, getAndSet
como operación atómica con booleano volátil (a menos que, por supuesto, lo sincronice).
AtomicBoolean
tiene métodos que realizan sus operaciones compuestas atómicamente y sin tener que usar un synchronized
bloque. Por otro lado, volatile boolean
solo puede realizar operaciones compuestas si lo hace dentro de un synchronized
bloque.
Los efectos de memoria de lectura / escritura volatile boolean
son idénticos a los métodos get
y set
de AtomicBoolean
respectivamente.
Por ejemplo, el compareAndSet
método realizará atómicamente lo siguiente (sin synchronized
bloque):
if (value == expectedValue) {
value = newValue;
return true;
} else {
return false;
}
Por lo tanto, el compareAndSet
método le permitirá escribir código que se garantiza que se ejecutará solo una vez, incluso cuando se lo llame desde varios subprocesos. Por ejemplo:
final AtomicBoolean isJobDone = new AtomicBoolean(false);
...
if (isJobDone.compareAndSet(false, true)) {
listener.notifyJobDone();
}
Se garantiza que solo notificará al oyente una vez (suponiendo que ningún otro subproceso establezca la parte AtomicBoolean
posterior false
nuevamente después de que se configure en true
).
volatile
La palabra clave garantiza la relación de suceso previo entre hilos que comparten esa variable No garantiza que 2 o más subprocesos no se interrumpan entre sí al acceder a esa variable booleana.
Atomic*
clase envuelve un volatile
campo.
Boolean volátil vs AtomicBoolean
Las clases Atomic * envuelven una primitiva volátil del mismo tipo. De la fuente:
public class AtomicLong extends Number implements java.io.Serializable {
...
private volatile long value;
...
public final long get() {
return value;
}
...
public final void set(long newValue) {
value = newValue;
}
Entonces, si todo lo que está haciendo es obtener y configurar un Atomic *, entonces también podría tener un campo volátil.
¿Qué hace AtomicBoolean que un booleano volátil no puede lograr?
Las clases Atomic * le brindan métodos que proporcionan una funcionalidad más avanzada, como incrementAndGet()
, compareAndSet()
y otros que implementan múltiples operaciones (obtener / incrementar / establecer, probar / establecer) sin bloqueo. Es por eso que las clases Atomic * son tan poderosas.
Por ejemplo, si varios subprocesos usan el siguiente código ++
, habrá condiciones de carrera porque en ++
realidad es: obtener, incrementar y establecer.
private volatile value;
...
// race conditions here
value++;
Sin embargo, el siguiente código funcionará en un entorno multiproceso de forma segura sin bloqueos:
private final AtomicLong value = new AtomicLong();
...
value.incrementAndGet();
También es importante tener en cuenta que ajustar su campo volátil con la clase Atomic * es una buena manera de encapsular el recurso crítico compartido desde el punto de vista de un objeto. Esto significa que los desarrolladores no pueden simplemente lidiar con el campo suponiendo que no se comparte, posiblemente inyectando problemas con un campo ++; u otro código que introduzca condiciones de carrera.
Si hay varios subprocesos que acceden a la variable de nivel de clase, cada subproceso puede mantener una copia de esa variable en su caché local de subprocesos.
Hacer que la variable sea volátil evitará que los hilos mantengan la copia de la variable en la caché local de hilos.
Las variables atómicas son diferentes y permiten la modificación atómica de sus valores.
El tipo primitivo booleano es atómico para operaciones de escritura y lectura, volátil garantiza el principio de antes. Entonces, si necesita un simple get () y set (), entonces no necesita el AtomicBoolean.
Por otro lado, si necesita implementar alguna verificación antes de establecer el valor de una variable, por ejemplo, "si es verdadero, entonces se establece en falso", entonces también debe realizar esta operación atómicamente, en este caso, use compareAndSet y otros métodos proporcionados por AtomicBoolean, ya que si intenta implementar esta lógica con boolean volátil, necesitará cierta sincronización para asegurarse de que el valor no haya cambiado entre get y set.
Recuerda la IDIOMA
LEER - MODIFICAR- ESCRIBIR esto no se puede lograr con volátil
volatile
solo funciona en los casos en que el subproceso propietario tiene la capacidad de actualizar el valor del campo y los otros subprocesos solo pueden leer.
Si solo tiene un hilo que modifica su booleano, puede usar un booleano volátil (generalmente lo hace para definir una stop
variable marcada en el bucle principal del hilo).
Sin embargo, si tiene múltiples hilos modificando el booleano, debe usar un AtomicBoolean
. De lo contrario, el siguiente código no es seguro:
boolean r = !myVolatileBoolean;
Esta operación se realiza en dos pasos:
Si otro hilo modifica el valor entre #1
y 2#
, es posible que obtenga un resultado incorrecto. AtomicBoolean
Los métodos evitan este problema haciendo pasos #1
y #2
atómicamente.
Ambos son del mismo concepto, pero en booleano atómico proporcionará atomicidad a la operación en caso de que el interruptor de la CPU ocurra en el medio.