Volatile boolean vs AtomicBoolean

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

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

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

Si dos hilos llaman a la función al mismo tiempo, podría ser 5 después, ya que el código comstackdo será algo similar a esto (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 está sincronizado, pero no siempre es obvio lo que realmente califica como acceso atómico. Con un objeto Atomic* , se garantiza que todos los métodos sean “atómicos”.

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 simultáneamente una variable boolean , con un AtomicBoolean puedes estar seguro de que tiene el valor original después, con un volatile boolean , no puedes.

Por lo tanto, cada vez 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 ejecutando un loop() y otro subproceso que invoca a la stop() , puede ejecutar un bucle infinito si omite el volatile , ya que el primer subproceso podría almacenar en caché el valor de detención. Aquí, lo volatile sirve como una pista para que el comstackdor sea un poco más cuidadoso con las optimizaciones.

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 publicar / suscribir donde hay muchos observadores pero solo un editor. Sin embargo, si esos observadores deben realizar una lógica basada en el valor del campo y luego retrotraer un nuevo valor, entonces voy con Atomic * vars o lockings o bloques sincronizados, lo que más me convenga. En muchos escenarios concurrentes, se reduce para 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 las clases Atomic y una excelente explicación de cómo funcionan (solo se dio cuenta de que están libres de lockings, por lo que tienen una ventaja sobre los lockings o bloques sincronizados)

No puede hacer 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 de forma atómica y sin tener que usar un bloque synchronized . Por otro lado, volatile boolean solo puede realizar operaciones compuestas si se realiza 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 lo llame desde múltiples hilos. 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 hilo vuelva a configurar AtomicBoolean en false una vez que se establezca en true ).

volatile palabra clave volatile garantiza la relación de las relaciones pasadas entre los hilos que comparten esa variable. No garantiza que 2 o más hilos no se interrumpan mientras acceden a esa variable booleana.

Si hay múltiples subprocesos que acceden a la variable de nivel de clase, cada subproceso puede mantener una copia de esa variable en su caché threadlocal.

Hacer que la variable sea volátil evitará que los subprocesos conserven la copia de la variable en la caché de threadlocal.

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

El tipo primitivo booleano es atómico para las operaciones de escritura y lectura, volátil garantiza el principio de pasar antes. Entonces, si necesita un simple get () y set (), entonces no necesita AtomicBoolean.

Por otro lado, si necesita implementar alguna comprobación antes de establecer el valor de una variable, por ejemplo, “si es verdadero y luego establecerlo 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 intentas implementar esta lógica con booleano volátil, necesitarás cierta sincronización para asegurarte de que el valor no haya cambiado entre get y set.

Volatile boolean 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 hace es obtener y configurar un atómico *, 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 le dan las clases Atomic * son métodos que brindan una funcionalidad más avanzada, como incrementAndGet() , compareAndSet() y otras que implementan múltiples operaciones (get / increment / set, test / set) sin locking. Es por eso que las clases Atomic * son muy poderosas.

Por ejemplo, si varios subprocesos usan el siguiente código con ++ , habrá condiciones de carrera porque ++ es en realidad: get, increment y set.

 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 forma de encapsular el recurso compartido crítico desde el punto de vista de un objeto. Esto significa que los desarrolladores no pueden tratar el campo suponiendo que no se comparte, posiblemente inyectando problemas con un campo ++; u otro código que introduce condiciones de carrera.

Recuerda el IDIOM –

LEER – MODIFICAR- ESCRIBA esto que no puede lograr con volátiles

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

Sin embargo, si tiene varios hilos 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. El valor booleano es leído.
  2. El valor booleano está escrito.

Si otro subproceso 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.

Intereting Posts