java - Volatile boolean vs atomic boolean




concurrency atomicboolean (9)

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


Answers

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.


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 ).


Utilizo campos volátiles cuando dicho campo SOLO 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 rechazar un nuevo valor, entonces voy con Atomic * vars o bloqueos o bloques sincronizados, lo que más me convenga. En muchos escenarios concurrentes, se reduce a obtener el valor, se compara con otro y se actualiza si es necesario, por lo tanto, los métodos compareAndSet y getAndSet presentes en las clases Atomic *.

Revise los JavaDocs del paquete java.util.concurrent.atomic para obtener una lista de las clases atómicas y una excelente explicación de cómo funcionan (recién aprendí que están libres de bloqueo, por lo que tienen una ventaja sobre los bloqueos o bloques sincronizados)


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.


Si solo tiene un hilo que modifica su booleano, puede usar un booleano volátil (por lo general, lo hace para definir una variable de stop marcada en el bucle principal del hilo).

Sin embargo, si tiene varios subprocesos que modifican 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:

  1. Se lee el valor booleano.
  2. Se escribe el valor booleano.

Si otro hilo modifica el valor entre #1 y 2# , es posible que obtenga un resultado incorrecto. AtomicBoolean métodos AtomicBoolean evitan este problema haciendo los pasos #1 y #2 atómicamente.


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.


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.


Recuerda el IDIOM -

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


Como siempre con estas preguntas, el JLS tiene la respuesta. En este caso, §15.26.2 Operadores de asignación de compuestos . Un extracto:

Una expresión de asignación compuesta de la forma E1 op= E2 es equivalente a E1 = (T)((E1) op (E2)) , donde T es el tipo de E1 , excepto que E1 se evalúa solo una vez.

Un ejemplo citado de §15.26.2

[...] el siguiente código es correcto:

short x = 3;
x += 4.6;

y da como resultado que x tenga el valor 7 porque es equivalente a:

short x = 3;
x = (short)(x + 4.6);

En otras palabras, tu suposición es correcta.





java concurrency boolean volatile atomicboolean