¿Cuándo necesito usar AtomicBoolean en Java?

Respuestas:

245

Cuando múltiples hilos necesitan verificar y cambiar el booleano. Por ejemplo:

if (!initialized) {
   initialize();
   initialized = true;
}

Esto no es seguro para subprocesos. Puedes arreglarlo usando AtomicBoolean:

if (atomicInitialized.compareAndSet(false, true)) {
    initialize();
}
Bozho
fuente
51
No parece un ejemplo del mundo real: otro hilo puede ver truecuando initialize()no se ha completado. Por lo tanto, funciona solo si otros hilos no se preocupan por completarlos initialize().
axtavt
66
@axtavt: Creo que es un ejemplo del mundo real perfectamente válido si initializedsimplemente se está utilizando para garantizar que un solo hilo invoque el initialize()método. Obviamente, initializedser cierto no significa que la inicialización se haya completado definitivamente en este caso, por lo que quizás un término ligeramente diferente sería mejor aquí. De nuevo, depende de para qué se esté utilizando.
ColinD
14
necesitaría 2 booleanos para initStarted e initCompleted, luego el primer subproceso establece initStarted y llama a initialise (), el resto espera hasta que initCompleted sea verdadero.
Martin
3
@Bozho: las lecturas y escrituras en los campos booleanos son atómicas, ¿verdad ?, ahora, volátil me da el último valor del campo booleano. Entonces, efectivamente, ¿no volatile booleansería lo mismo que AtomicBoolean?
TheLostMind
2
@ Martin: No hay forma directa de esperar a que un booleano se convierta en realidad; Necesitas mecanismos adicionales. El enfoque más sensato es usar un synchronizedbloque, en cuyo caso ya no necesita un AtomicBoolean, solo un volatile boolean. ( if(! this.initialized) { synchronized(this) { if(! this.initialized) { initialize(); this.initialized = true; } } }se asegurará de que solo un subproceso llame initializey que todos los demás subprocesos esperen a que lo haga, siempre que initializedesté marcado volatile.)
ruakh
52

Aquí están las notas (del libro de Brian Goetz ) que hice, que podrían ser de ayuda para usted.

Clases AtomicXXX

  • Proporcionar implementación de comparación e intercambio sin bloqueo

  • Aprovecha el soporte proporcionado por el hardware (la instrucción CMPXCHG en Intel) Cuando se ejecutan muchos subprocesos a través de su código que usa estas API de concurrencia atómica, se escalarán mucho mejor que el código que usa monitores / sincronización de nivel de Objeto. Dado que los mecanismos de sincronización de Java hacen que el código espere, cuando hay muchos subprocesos que se ejecutan en sus secciones críticas, se dedica una cantidad considerable de tiempo de CPU a administrar el mecanismo de sincronización en sí (esperar, notificar, etc.). Dado que la nueva API utiliza construcciones de nivel de hardware (variables atómicas) y algoritmos de espera y bloqueo para implementar seguridad de subprocesos, se dedica mucho más tiempo de CPU a "hacer cosas" en lugar de administrar la sincronización.

  • no solo ofrecen un mejor rendimiento, sino que también brindan una mayor resistencia a los problemas de vida como el punto muerto y la inversión prioritaria.

Aravind Yarram
fuente
34

Hay dos razones principales por las que puede usar un booleano atómico. Primero es mutable, puede pasarlo como referencia y cambiar el valor asociado al booleano, por ejemplo.

public final class MyThreadSafeClass{

    private AtomicBoolean myBoolean = new AtomicBoolean(false);
    private SomeThreadSafeObject someObject = new SomeThreadSafeObject();

    public boolean doSomething(){
         someObject.doSomeWork(myBoolean);
         return myBoolean.get(); //will return true
    }
}

y en la clase someObject

public final class SomeThreadSafeObject{
    public void doSomeWork(AtomicBoolean b){
        b.set(true);
    }
}

Sin embargo, lo más importante es que es seguro para subprocesos y puede indicar a los desarrolladores que mantienen la clase que se espera que esta variable sea modificada y leída desde múltiples subprocesos. Si no utiliza un AtomicBoolean, debe sincronizar la variable booleana que está utilizando declarándola volátil o sincronizando la lectura y escritura del campo.

John Vint
fuente
44
Por el amor de Dios, eso fue solo para mostrar la mutabilidad del objeto mismo. Lo escribí específicamente con fines de demostración.
John Vint
Y aún más, si eso fue TODO lo que estaba sucediendo, entonces sí, siempre volverá a ser cierto
John Vint, el
Eso no prueba si es o no es seguro para subprocesos. Puedo terminar mis fragmentos de código para hacer que la clase sea muy segura para subprocesos, pero eso solo mata mi punto.
John Vint
1
Creo que solo volátil no es suficiente. Piense en una situación en la que dos hilos que leen y escriben el mismo valor directamente desde la memoria principal, no hay sincronización entre esos hilos, ya que pueden surgir problemas de concurrencia.
Shay Tsadok
1
Tienes razón, no sería suficiente para el conjunto atómico y luego verificar las operaciones, aunque no hubo suficiente contexto del OP para hacer esa suposición. Decir que la volatilidad podría no ser suficiente siempre es cierto dependiendo de la situación, por supuesto.
John Vint
5

Extracto de la descripción del paquete

Descripción del paquete java.util.concurrent.atomic: un pequeño conjunto de herramientas de clases que admite la programación segura de subprocesos sin bloqueo en variables individuales. [...]

Las especificaciones de estos métodos permiten que las implementaciones empleen instrucciones atómicas eficientes a nivel de máquina que están disponibles en los procesadores contemporáneos.

Las instancias de las clases AtomicBoolean, AtomicInteger, AtomicLong y AtomicReference proporcionan acceso y actualizaciones a una sola variable del tipo correspondiente. [...]

Los efectos de memoria para accesos y actualizaciones de atómicos generalmente siguen las reglas para volátiles:

  • get tiene los efectos de memoria de leer una variable volátil.
  • set tiene los efectos de memoria de escribir (asignar) una variable volátil.
  • weakCompareAndSet lee atómicamente y escribe condicionalmente una variable, se ordena con respecto a otras operaciones de memoria en esa variable, pero por lo demás actúa como una operación de memoria no volátil ordinaria.
  • compareAndSet y todas las demás operaciones de lectura y actualización, como getAndIncrement, tienen los efectos de memoria de leer y escribir variables volátiles.
OscarRyz
fuente