java concurrency boolean volatile atomicboolean

java - Volatile boolean vs atomic boolean



concurrency atomicboolean (10)

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


No puede hacer compareAndSet , getAndSet como operación atómica con booleano volátil (a menos que, por supuesto, lo sincronice).


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.


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)


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.


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.


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.