Volatile booleano vs AtomicBoolean


245

Cosa fa AtomicBoolean che un booleano volatile non può ottenere?


16
Stavo cercando una risposta più sfumata a: "quali sono i limiti di ciascuno?". Ad esempio, se is è un flag impostato da un thread e letto da uno o più altri, AtomicBoolean non è necessario. Tuttavia, come sto vedendo con queste risposte, se il thread condivide una variabile in più thread può scrivere e agisce sul risultato delle loro letture, AtomicBoolean mette in gioco le operazioni non bloccanti di tipo CAS. Sto imparando un po 'qui, in realtà. Speriamo che anche altri ne trarranno beneficio.
JeffV,

1
Flow

il booleano volatile avrà bisogno di una sincronizzazione esplicita per gestire le condizioni di competizione, in altre parole, lo scenario come la risorsa condivisa viene aggiornato (cambio di stato) da più thread, ad esempio contatore di incremento / decremento o lancio booleano.
Sactiw,

Risposte:


98

Sono solo totalmente diversi. Considera questo esempio di un volatilenumero intero:

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

Se due thread chiamano contemporaneamente la funzione, ipotrebbero essere 5 in seguito, poiché il codice compilato sarà in qualche modo simile a questo (tranne che non è possibile sincronizzare su int):

void incIBy5() {
    int temp;
    synchronized(i) { temp = i }
    synchronized(i) { i = temp + 5 }
}

Se una variabile è volatile, ogni accesso atomico ad essa è sincronizzato, ma non è sempre ovvio ciò che effettivamente si qualifica come accesso atomico. Con un Atomic*oggetto, è garantito che ogni metodo è "atomico".

Quindi, se usi un AtomicIntegere getAndAdd(int delta), puoi essere sicuro che il risultato sarà 10. Allo stesso modo, se due thread negano booleancontemporaneamente una variabile, con un AtomicBooleanpuoi essere sicuro che abbia il valore originale in seguito, con un volatile boolean, non puoi.

Quindi ogni volta che hai più di un thread che modifica un campo, devi renderlo atomico o usare la sincronizzazione esplicita.

Lo scopo di volatileè diverso. Considera questo esempio

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

Se hai un thread in esecuzione loop()e un altro thread chiama stop(), potresti ometterlo in un ciclo infinito se ometti volatile, poiché il primo thread potrebbe memorizzare nella cache il valore di stop. Qui, volatileserve come suggerimento al compilatore per essere un po 'più attento con le ottimizzazioni.


84
-1: stai dando esempi ma non stai davvero spiegando la differenza tra un volatile e un Atomicxxxx.
Jason S,

70
La domanda non riguarda volatile. La domanda è di circa volatile booleanvs AtomicBoolean.
dolmen,

26
-1: la domanda specificamente posta su booleano, che è un caso unico rispetto agli altri tipi di dati e dovrebbe essere spiegato direttamente.
John Haager,

8
@ sgp15 Ha a che fare con la sincronizzazione a partire da Java 5.
Man of One Way

6
Se il valore booleano viene letto da molti thread, ma scritto da un solo thread, volatile booleanè sufficiente. Se ci sono anche molti scrittori, potresti averne bisogno AtomicBoolean.
StvnBrkdll,

263

Uso campi volatili quando il campo è AGGIORNATO SOLO dal thread del proprietario e il valore viene letto solo da altri thread, puoi considerarlo uno scenario di pubblicazione / sottoscrizione in cui ci sono molti osservatori ma un solo editore. Tuttavia, se quegli osservatori devono eseguire una logica basata sul valore del campo e quindi respingere un nuovo valore, allora vado con Atomic * varianti o blocchi o blocchi sincronizzati, qualunque cosa mi si adatti meglio. In molti scenari simultanei si riduce per ottenere il valore, confrontarlo con un altro e aggiornarlo se necessario, quindi i metodi compareAndSet e getAndSet presenti nelle classi Atomic *.

Controlla i JavaDocs del pacchetto java.util.concurrent.atomic per un elenco di classi Atomic e un'eccellente spiegazione del loro funzionamento (ho appena appreso che sono senza blocchi, quindi hanno un vantaggio rispetto ai blocchi o ai blocchi sincronizzati)


1
@ksl Penso che @teto voglia descrivere che se solo un thread modifica il booleanvar, dovremmo scegliere volatile boolean.
znlyj,

2
Riepilogo eccellente.
Ravindra babu,

56

Non puoi farlo compareAndSet, getAndSetcome operazione atomica con valore booleano volatile (a meno che ovviamente non lo sincronizzi).


6
Questo è vero, ma non sarebbe un requisito piuttosto raro per un booleano?
Robin,

1
@Robin pensa a usarlo per controllare una chiamata pigra di un metodo di inizializzazione.
Ustaman Sangat,

In realtà, penso che questo sia uno dei principali casi d'uso.
fool4jesus

42

AtomicBooleanha metodi che eseguono le operazioni composte atomicamente e senza dover usare un synchronizedblocco. D'altra parte, volatile booleanpuò eseguire operazioni composte solo se fatto all'interno di un synchronizedblocco.

Gli effetti di memoria della lettura / scrittura volatile booleansono identici ai metodi gete setdi AtomicBooleanrispettivamente.

Ad esempio, il compareAndSetmetodo eseguirà atomicamente quanto segue (senza un synchronizedblocco):

if (value == expectedValue) {
    value = newValue;
    return true;
} else {
    return false;
}

Quindi, il compareAndSetmetodo ti permetterà di scrivere codice che è garantito per essere eseguito una sola volta, anche quando chiamato da più thread. Per esempio:

final AtomicBoolean isJobDone = new AtomicBoolean(false);

...

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

È garantito per notificare solo l'ascoltatore una volta (supponendo che non altri set di filo il AtomicBooleannuovo a falsenuovo dopo che sia impostato su true).


14

volatilela parola chiave garantisce la relazione prima dei thread che condividono quella variabile. Non ti garantisce che 2 o più thread non si interrompano a vicenda durante l'accesso a quella variabile booleana.


14
L'accesso booleano (come nel tipo primitivo) è atomico in Java. Letture e compiti. Quindi nessun altro thread "interromperà" le operazioni booleane.
Maciej Biłas,

1
Mi dispiace ma come risponde alla domanda? Una Atomic*classe avvolge un volatilecampo.
Grey,

La cache della CPU non è il fattore principale per l'impostazione della volatilità? Per garantire che il valore letto sia effettivamente quello che è stato impostato più di recente
giocate l'

8

Volatile booleano vs AtomicBoolean

Le classi Atomic * avvolgono una primitiva volatile dello stesso tipo. Dalla fonte:

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

Quindi, se tutto ciò che stai facendo è ottenere e impostare un Atomic *, potresti anche avere un campo volatile.

Cosa fa AtomicBoolean che un booleano volatile non può ottenere?

Le classi Atomic * offrono metodi che forniscono funzionalità più avanzate come incrementAndGet(), compareAndSet()e altre che implementano più operazioni (get / increment / set, test / set) senza blocco. Ecco perché le classi Atomic * sono così potenti.

Ad esempio, se più thread utilizzano il seguente codice utilizzando ++, ci saranno condizioni di competizione perché ++è effettivamente: get, increment, e set.

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

Tuttavia, il codice seguente funzionerà in un ambiente multi-thread sicuro senza blocchi:

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

È anche importante notare che il wrapping del campo volatile usando la classe Atomic * è un buon modo per incapsulare la risorsa condivisa critica dal punto di vista degli oggetti. Ciò significa che gli sviluppatori non possono limitarsi a gestire il campo supponendo che non sia condiviso, probabilmente iniettando problemi con un campo ++; o altro codice che introduce condizioni di gara.


5

Se ci sono più thread che accedono alla variabile a livello di classe, ogni thread può conservare una copia di quella variabile nella sua cache locale.

Rendere la variabile volatile impedirà ai thread di mantenere la copia della variabile nella cache dei thread locali.

Le variabili atomiche sono diverse e consentono la modifica atomica dei loro valori.


5

Il tipo primitivo booleano è atomico per le operazioni di scrittura e lettura, volatile garantisce il principio di successo. Quindi, se hai bisogno di un semplice get () e set (), allora non hai bisogno di AtomicBoolean.

D'altra parte, se è necessario implementare un controllo prima di impostare il valore di una variabile, ad esempio "se true, quindi impostare su false", è necessario eseguire anche questa operazione in modo atomico, in questo caso utilizzare compareAndSet e altri metodi forniti da AtomicBoolean, poiché se si tenta di implementare questa logica con valori booleani volatili è necessario un po 'di sincronizzazione per assicurarsi che il valore non sia cambiato tra get e set.


3

Ricorda l'IDIOM -

LEGGI - MODIFICA - SCRIVI ciò che non puoi ottenere con volatile


2
Corto, croccante e al punto. volatilefunziona solo nei casi in cui il thread proprietario ha la possibilità di aggiornare il valore del campo e gli altri thread possono solo leggere.
Chaklader Asfak Arefe,

3

Se hai un solo thread che modifica il tuo booleano, puoi usare un booleano volatile (di solito lo fai per definire una stopvariabile controllata nel ciclo principale del thread).

Tuttavia, se hai più thread che modificano il valore booleano, dovresti usare un AtomicBoolean. Altrimenti, il seguente codice non è sicuro:

boolean r = !myVolatileBoolean;

Questa operazione viene eseguita in due passaggi:

  1. Viene letto il valore booleano.
  2. Il valore booleano è scritto.

Se un altro thread modifica il valore tra #1e 2#, potresti ottenere un risultato errato. AtomicBooleani metodi evitano questo problema facendo passi #1e #2atomicamente.


"Se hai un solo thread che modifica il tuo booleano, puoi usare un booleano volatile." Quindi se usi un thread, perché dovresti aver bisogno di un volatile (?) .. Dovresti rimuovere il primo paragrafo per migliorare la risposta ..
minsk

-1

Entrambi sono dello stesso concetto, ma in booleano atomico forniranno atomicità all'operazione nel caso in cui l'interruttore cpu avvenga nel mezzo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.