2010-09-24 22 views
177

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

+13

Estaba buscando una respuesta más matizada a: "¿cuáles son las limitaciones de cada uno?". Por ejemplo, si se trata de un indicador establecido por un hilo y leído por uno o más, no es necesario AtomicBoolean. Sin embargo, como estoy viendo con estas respuestas, si el hilo está compartiendo una variable en un hilo múltiple que puede escribir y está actuando sobre el resultado de sus lecturas, AtomicBoolean pone en juego operaciones de tipo CAS sin bloqueo. Estoy aprendiendo bastante aquí, en realidad. Con suerte, otros se beneficiarán también. – JeffV

+1

posible duplicado de [volatile boolean vs. AtomicBoolean] (http://stackoverflow.com/questions/4876122/volatile-boolean-vs-atomicboolean) – Flow

+0

volátil booleano necesitará sincronización explícita para manejar las condiciones de carrera, en otras palabras, escenario como recurso compartido que se actualiza (cambio de estado) por múltiples hilos, por ejemplo contador de incremento/decremento o volteo booleano. – sactiw

Respuesta

79

Son totalmente diferentes. Considere este ejemplo de un volatile entero:

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

Si dos subprocesos llaman a la función al mismo tiempo, i podría ser 5 después, ya que el código compilado 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 se sincroniza, pero no siempre es obvio lo que realmente califica como un acceso atómico. Con un objeto Atomic*, se garantiza que todos los métodos sean "atómicos".

Por lo tanto, si utiliza un AtomicInteger y getAndAdd(int delta), puede estar seguro de que el resultado será 10. De la misma manera, si dos hilos niegan simultáneamente una variable boolean, con un AtomicBoolean puede estar seguro de que tiene el valor original después, con un volatile boolean, no puede.

Así que 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 uno diferente. Considere este ejemplo

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

Si usted tiene un hilo conductor loop() y otro hilo llamando stop(), usted podría encontrarse en un bucle infinito si se omite volatile, ya que el primer hilo podría almacenar en caché el valor de parada. Aquí, el volatile sirve como una pista para que el compilador sea un poco más cuidadoso con las optimizaciones.

+64

-1: está dando ejemplos pero no explica la diferencia entre un volátil y un Atomicxxxx. –

+0

Por favor, eche un vistazo a la pregunta original. "Permitir modificaciones atómicas" es una respuesta válida. – Cephalopod

+41

La pregunta no es sobre 'volátil'. La pregunta es sobre 'boolean volátil' contra' AtomicBoolean'. – dolmen

44

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

+3

Esto es cierto, pero ¿no sería este un requisito bastante raro para un booleano? – Robin

+0

@Robin piensa en usarlo para controlar una invocación diferida de un método de inicialización. –

11

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

+11

El acceso booleano (como en el tipo primitivo) es atómico en Java. Ambas lecturas y asignaciones. Por lo tanto, ningún otro hilo "interrumpirá" las operaciones booleanas. –

+2

@ maciej-bilas Una referencia a la especificación de JVM sería bienvenida. – dolmen

+1

Lo siento, pero ¿cómo responde esto a la pregunta? Una clase 'Atomic *' envuelve un campo 'volátil'. – Gray

220

Uso campos volátiles cuando dicho campo SÓLO ESTÁ 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 una lógica basada en el valor del campo y luego retrotraer 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 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 *.

Comprobar los JavaDocs del paquete java.util.concurrent.atomic para obtener una lista de las clases atómico y una excelente explicación de cómo funcionan (acaba de aprender que son cerradura-libre, por lo que tienen una ventaja sobre las cerraduras o bloques sincronizados)

+0

¿Cómo se decide qué hilo es el hilo del propietario? – ksl

+1

@ksl Creo que @te quiere describir que si solo un hilo modifica la var 'boolean', deberíamos elegir' ​​volátil boolean'. – znlyj

+0

Excelente resumen. –

2

Recuerde que el lenguaje -

LEER - Modificar- escribir esto no se puede lograr con volátil

5

Si hay varios hilos de acceso variable de nivel de clase a continuación cada hilo puede mantener copia de esa variable en su caché ThreadLocal.

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

37

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 lo hace dentro de un bloque synchronized.

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

Por ejemplo atómicamente llevará a cabo el método compareAndSet 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 está garantizado para ejecutar sólo una vez, incluso cuando se llama desde múltiples hilos. Por ejemplo:

final AtomicBoolean isJobDone = new AtomicBoolean(false); 

... 

if (isJobDone.compareAndSet(false, true)) { 
    listener.notifyJobDone(); 
} 

se garantiza que sólo se notificará al oyente una vez (suponiendo que no hay otro hilo establece el AtomicBoolean de nuevo a false de nuevo después de haber sido ajustada a true).

2

El tipo primitivo booleano es atómico para 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, p. "si es verdadero y luego establezca 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 booleano volátil, necesitará alguna sincronización para asegúrese de que el valor no haya cambiado entre get y set.

2

Si usted tiene sólo un hilo de modificar su booleano, se puede utilizar una volátil booleano (por lo general lo hace para definir una variable stop comprobado en bucle principal de la rosca).

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: se lee

  1. El valor booleano.
  2. El valor booleano está escrito.

Si otro hilo de modificar el valor entre #1 y 2#, es posible que consiguió un resultado erróneo. Los métodos AtomicBoolean evitan este problema siguiendo los pasos #1 y #2 atómicamente.

2

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; 
    } 

Así que si todo lo que está haciendo es obtener y establecer una Atómica * entonces puede ser que también acaba de tener un campo volátil en su lugar.

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

Lo que los atómicos * clases se dan sin embargo, son métodos que proporcionan una funcionalidad más avanzada, como incrementAndGet(), compareAndSet(), y otros que implementan múltiples operaciones (obtener/incremento/set, prueba/set) sin bloquear. Es por eso que las clases Atomic * son muy poderosas.

Por ejemplo, el siguiente código funcionará en un entorno multi-hilo con seguridad:

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

Sin embargo, si varios subprocesos están utilizando la siguiente, habrá condiciones de carrera, porque ++ es en realidad: obtener, incremento , y establecer.

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

También es importante tener en cuenta que si se envuelve su campo volátiles utilizando Atómica * clase es una buena forma de encapsular el recurso crítico compartida desde un punto de vista objetivo. 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.