2012-10-12 155 views
15

Duplicar posible:
Java: volatile boolean vs AtomicBoolean¿Cuál es la diferencia entre usar una primitiva volátil sobre variables atómicas?

cuándo es apropiado utilizar un volatile primitiva (por ejemplo boolean, integer o long) en lugar de AtomicBoolean, AtomicInteger o AtomicLong, y vice-versa?

+0

Dado que uno lo ha mencionado (ni aquí ni en el duplicado): con las clases Atomic tienes un ingenioso método lazySet. En la mayoría de las arquitecturas supera a la escritura volátil básica (pero no tiene la misma semántica). Básicamente, lazySet no necesita vaciar los búferes de escritura de la CPU, lo cual es bastante bueno ya que pueden enjuagarse mientras la CPU está detenida. – bestsss

Respuesta

17

La semántica de visibilidad es exactamente la misma, la situación en la que es útil utilizar las primitivas atómicas es cuando necesita utilizar sus métodos atómicos.

Por ejemplo:

if (volatileBoolean) { 
    volatileBoolean = !volatileBoolean; 
} 

podría crear problemas en un entorno multihilo como la variable puede cambiar entre las dos líneas. Si necesita la asignación de prueba & ser atómicas, puede utilizar:

atomicBoolean.compareAndSet(true, false); 
+6

'volatileBoolean =! VolatileBoolean;' puede tener un problema de subprocesamiento por sí mismo. –

+0

@PeterLawrey Buen punto. – assylias

+1

¿Porque negar el booleano es una operación de dos pasos? –

12

usando una volátil llanura es más simple y más eficiente si todo lo que quiere hacer es establecer u obtener el valor. (Pero no obtenga & establezca el valor)

Si desea realizar más operaciones como getAndIncrement o compare Y Swap, debe utilizar las clases AtomicXxxx.

+1

Gracias por su respuesta –

+0

lazySet on atomic generalmente gana sobre 'escritura' volátil (especialmente en arquitecturas TSO) pero tiene una semántica diferente. – bestsss

+0

lazySet es más rápido ya que no detiene la tubería de la CPU. –

7

Hemos comenzado a prohibir el uso de volatile en nuestras fuentes porque es muy fácil escribir código que no siempre funciona como se esperaba.

En mi experiencia, las personas agregan volátiles para compartir un valor entre subprocesos. Y luego, alguien más comienza a modificar el valor. Y la mayor parte del tiempo, esto funciona. Pero en producción, comienzas a obtener errores extraños que son realmente difíciles de rastrear. Los contadores se incrementan 100'000 veces (las pruebas solo los incrementan 10 veces) pero terminan en 99'997. En Java 1.4, los valores largos podrían corromperse realmente, muy raramente.

Las clases de ayuda Atomic*, por otro lado, imponen solo una pequeña sobrecarga y siempre funcionan como se anuncia.

De modo que a menos que tenga una muy buena razón (*) para usar volatile, siempre prefiera las clases de ayuda Atomic*.

Si no sabe exactamente lo que hace cada carácter en las clases de ayuda Atomic*, entonces realmente debe evitar volatile.

*: La optimización prematura nunca es una buena razón.

+0

Gracias por su respuesta –

+0

* "es muy fácil escribir código que no siempre funciona como se esperaba." * => ¿Podría ser más específico? ¿Quiere decir que las personas que asumen la volatilidad ofrecen garantías de atomicidad, por ejemplo? – assylias

+1

@assylias: Agregué algunos ejemplos a mi respuesta. –

7

Las clases Atomic* envuelven una primitiva del mismo tipo volatile. Desde 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; 
    } 

So.

Cuándo es apropiado utilizar una primitiva volátil (p.booleano, entero o larga) en lugar de AtomicBoolean, AtomicInteger o AtomicLong

Si todo lo que está haciendo es obtener y establecer un Atomic* entonces puede ser que también acaba de tener un campo volatile lugar.

... y viceversa?

Lo que los Atomic* 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 bloqueo. Es por eso que las clases Atomic* son tan poderosas.

También es importante tener en cuenta que envolver su campo volatile utilizando 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 suponiendo que no se comparte, posiblemente inyectando problemas con un field++; u otro código que introduce condiciones de carrera.

+1

Gracias por su respuesta –

Cuestiones relacionadas