2012-10-12 8 views
15

Eventuali duplicati:
Java: volatile boolean vs AtomicBooleanQual è la differenza tra l'uso di una primitiva volatile rispetto alle variabili atomiche?

Quando è opportuno utilizzare un volatile primitiva (ad esempio boolean, integer o long) anziché AtomicBoolean, AtomicInteger o AtomicLong, e viceversa?

+0

Dal momento che uno lo ha menzionato (né qui né sul duplicato): w/classi atomiche si ha un metodo elegante lazySet. Sulla maggior parte delle architetture supera la scrittura volatile di base (ma non ha la stessa semantica). Fondamentalmente lazySet non ha bisogno di svuotare i buffer di scrittura della CPU, il che è abbastanza buono in quanto possono essere scaricati mentre la CPU è in stallo. – bestsss

risposta

17

La semantica della visibilità è esattamente la stessa, la situazione in cui è utile utilizzare i primitivi atomici è quando è necessario utilizzare i loro metodi atomici.

Ad esempio:

if (volatileBoolean) { 
    volatileBoolean = !volatileBoolean; 
} 

potrebbe creare problemi in un ambiente multi threaded come variabile potrebbe cambiare tra le due linee. Se è necessario l'assegnazione di prova & essere atomica, è possibile utilizzare:

atomicBoolean.compareAndSet(true, false); 
+6

'volatileBoolean =! VolatileBoolean;' può avere un problema di threading da solo. –

+0

@PeterLawrey Buon punto. – assylias

+1

Perché negare il valore booleano è un'operazione in due passaggi? –

12

usando una pianura volatili è più semplice e più efficiente, se tutto quello che vogliamo fare è impostare o ottenere il valore. (Ma non ottenere & impostare il valore)

Se si desidera più operazioni come getAndIncrement o compareAndSwap è necessario utilizzare le classi AtomicXxxx.

+1

Grazie per la risposta –

+0

lazySet su atomic di solito vince su "write" volatile (specialmente su architetture TSO) ma ha semantica diversa. – bestsss

+0

lazySet è più veloce in quanto non blocca la pipeline della CPU. –

7

Abbiamo iniziato a vietare l'uso di volatile nelle nostre fonti perché è molto semplice scrivere codice che non sempre funziona come previsto.

Nella mia esperienza, le persone aggiungono volatilità per condividere un valore tra i thread. E poi, qualcun altro inizia a modificare il valore. E la maggior parte delle volte funziona. Ma in produzione, inizi a ottenere errori strani che sono davvero difficili da rintracciare. I contatori vengono incrementati di 100'000 volte (i test li incrementano solo 10 volte) ma finiscono a 99'997. In Java 1.4, i valori lunghi potrebbero essere corrotti davvero, molto raramente.

Le classi di supporto Atomic*, d'altra parte, solo imporre un piccolo overhead e funzionano sempre come pubblicizzato.

Quindi, se non avete una buona ragione (*) da utilizzare volatile, preferire sempre le classi di supporto Atomic*.

Se non si sa esattamente che cosa ogni personaggio nelle classi di supporto Atomic* fa, allora si dovrebbe davvero evitare di volatile.

*: l'ottimizzazione prematura non è mai una buona ragione.

+0

Grazie per la risposta –

+0

* "è molto facile scrivere codice che non funziona sempre come previsto." * => Potresti essere più specifico? Intendi dire che le persone che assumono volatili danno garanzie di atomicità, ad esempio? – assylias

+1

@assylias: ho aggiunto alcuni esempi alla mia risposta. –

7

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

Così.

Quando è opportuno utilizzare una primitiva volatile (ad es.booleano, intero o lunga) al posto di AtomicBoolean, AtomicInteger o AtomicLong

Se tutti si sta facendo è ottenere e impostare un Atomic* allora si potrebbe anche semplicemente avere un campo volatile invece.

... e viceversa?

Quello che i Atomic* classi che danno tuttavia, sono metodi che forniscono funzionalità più avanzate come ad esempio incrementAndGet(), compareAndSet(), e altri che implementano operazioni multiple (get/incremento/set, test/set) senza bloccaggio. Ecco perché le classi Atomic* sono così potenti.

È anche importante notare che il wrapping del campo volatile utilizzando la classe Atomic* è un buon modo per incapsulare la risorsa condivisa fondamentale da un punto di vista dell'oggetto. Ciò significa che gli sviluppatori non possono semplicemente occuparsi del campo assumendo che non sia condiviso, eventualmente iniettando problemi con uno field++; o altro codice che introduce condizioni di competizione.

+1

Grazie per la risposta –

Problemi correlati