java - Volatile boolean vs atomic boolean




concurrency atomicboolean (7)

¿Qué hace AtomicBoolean que un booleano volátil no puede lograr?


Volatile boolean vs atomic boolean

Las clases Atómicas * 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;
   }

Por lo tanto, 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?

Sin embargo, lo que las clases Atomic * le brindan son métodos que proporcionan una funcionalidad más avanzada como incrementAndGet() , compareAndSet() y otros que implementan múltiples operaciones (obtener / incrementar / configurar, probar / configurar) sin bloqueo. Por eso las clases atómicas * son tan poderosas.

Por ejemplo, si varios subprocesos utilizan el siguiente código usando ++ , habrá condiciones de carrera porque ++ es en realidad: obtener, incrementar y establecer.

private volatile value;
...
// race conditions here
value++;

Sin embargo, el siguiente código funcionará en un entorno de subprocesos múltiples de forma segura:

private final AtomicLong value = new AtomicLong();
...
value.incrementAndGet();

También es importante tener en cuenta que envolver su campo volátil con la clase Atomic * es una buena manera de encapsular el recurso compartido crítico desde el punto de vista de un objeto. Esto significa que los desarrolladores no pueden lidiar con el campo asumiendo que no se comparte, posiblemente inyectando problemas con un campo ++; u otro código que introduzca condiciones de carrera.


El tipo primitivo booleano es atómico para las operaciones de escritura y lectura, y el principio volátil garantiza el principio de suceso. 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 establezca en falso", entonces también debe realizar esta operación de forma atómica, en este caso use compareAndSet y otros métodos proporcionados por AtomicBoolean, ya que si intenta implementar esta lógica con un booleano volátil, necesitará cierta sincronización para asegurarse de que el valor no haya cambiado entre obtener y establecer.


Recuerda el IDIOM -

LEA - MODIFICAR - ESCRIBA esto que no puede lograr con volatile


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é de ubicación de subproceso.

Si la variable es volátil, evitará que los subprocesos conserven la copia de la variable en el caché threadlocal.

Las variables atómicas son diferentes y permiten la modificación atómica de sus valores.


AtomicBoolean tiene métodos que realizan sus operaciones compuestas de forma atómica y sin tener que usar un bloque synchronized . Por otro lado, el volatile boolean solo puede realizar operaciones compuestas si se hace dentro de un bloque synchronized .

Los efectos de memoria de lectura / escritura en volatile boolean son idénticos a los métodos get y set de AtomicBoolean respectivamente.

Por ejemplo, el método compareAndSet realizará atómicamente lo siguiente (sin un bloque synchronized ):

if (value == expectedValue) {
    value = newValue;
    return true;
} else {
    return false;
}

Por lo tanto, el método compareAndSet le permitirá escribir código que se garantiza que se ejecutará solo una vez, incluso cuando se le 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 AtomicBoolean nuevo a false una vez que se haya establecido en true ).


volatile palabra clave volatile garantiza una relación antes de que suceda 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.


Ellos son totalmente diferentes. Considere este ejemplo de un entero volatile :

volatile int i = 0;
void incIBy5() {
    i += 5;
}

Si dos subprocesos llaman a la función al mismo tiempo, podría ser 5 después, ya que el código compilado será similar a este (excepto que no se puede sincronizar en 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 está sincronizado, pero no siempre es obvio lo que realmente califica como acceso atómico. Con un objeto Atomic* , 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 variable boolean simultáneamente, con un AtomicBoolean puede estar seguro de que tiene el valor original después, con un volatile boolean , no puede.

Entonces, cuando tenga más de un hilo modificando un campo, necesita hacerlo atómico o usar sincronización explícita.

El propósito de la volatile es diferente. Considera este ejemplo

volatile boolean stop = false;
void loop() {
    while (!stop) { ... }
}
void stop() { stop = true; }

Si tiene un subproceso que ejecuta loop() y otro subproceso que llama a stop() , es posible que se encuentre con un bucle infinito si omite volatile , ya que el primer subproceso podría almacenar en caché el valor de la detención. Aquí, la volatile sirve como una sugerencia para que el compilador sea un poco más cuidadoso con las optimizaciones.







atomicboolean