Risposte:
Quando più thread devono controllare e modificare il valore booleano. Per esempio:
if (!initialized) {
initialize();
initialized = true;
}
Questo non è thread-safe. Puoi risolverlo usando AtomicBoolean
:
if (atomicInitialized.compareAndSet(false, true)) {
initialize();
}
true
quando initialize()
non sono stati completati. Quindi, funziona solo se altri thread non si preoccupano del completamento di initialize()
.
initialized
viene semplicemente utilizzato per garantire che uno e un solo thread invochino il initialize()
metodo. Ovviamente initialized
essere vero non significa che l'inizializzazione sia stata definitivamente completata in questo caso, quindi forse un termine leggermente diverso sarebbe meglio qui. Ancora una volta, dipende da cosa viene utilizzato.
volatile boolean
stesso AtomicBoolean
?
synchronized
blocco, nel qual caso non è più necessario un AtomicBoolean
, solo un volatile boolean
. ( if(! this.initialized) { synchronized(this) { if(! this.initialized) { initialize(); this.initialized = true; } } }
assicurerà che solo un thread chiami initialize
e che tutti gli altri thread aspettino che ciò avvenga, a condizione che initialized
sia contrassegnato volatile
.)
Ecco le note (dal libro di Brian Goetz ) che ho fatto, che potrebbero esserti di aiuto
Classi AtomicXXX
fornire un'implementazione comparativa e di scambio non bloccante
Sfrutta il supporto fornito dall'hardware (le istruzioni CMPXCHG su Intel) Quando molti thread eseguono il codice che utilizza queste API di concorrenza atomica, si ridimensioneranno molto meglio del codice che utilizza i monitor / sincronizzazione a livello di oggetto. Poiché i meccanismi di sincronizzazione di Java fanno attendere il codice, quando ci sono molti thread in esecuzione attraverso le sezioni critiche, una notevole quantità di tempo della CPU viene impiegata nella gestione del meccanismo di sincronizzazione stesso (in attesa, notifica, ecc.). Poiché la nuova API utilizza costrutti a livello di hardware (variabili atomiche) e attende e blocca algoritmi liberi per implementare la sicurezza dei thread, si dedica molto più tempo alla CPU "facendo cose" piuttosto che a gestire la sincronizzazione.
non solo offrono una migliore produttività, ma forniscono anche una maggiore resistenza ai problemi di vivacità come deadlock e inversione di priorità.
Ci sono due ragioni principali per cui puoi usare un booleano atomico. Innanzitutto è modificabile, è possibile passarlo come riferimento e modificare il valore associato al booleano stesso, ad esempio.
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
}
}
e nella classe someObject
public final class SomeThreadSafeObject{
public void doSomeWork(AtomicBoolean b){
b.set(true);
}
}
Ancora più importante, il thread è sicuro e può indicare agli sviluppatori che mantengono la classe che questa variabile dovrebbe essere modificata e letta da più thread. Se non si utilizza un AtomicBoolean, è necessario sincronizzare la variabile booleana in uso dichiarandola volatile o sincronizzandosi con la lettura e la scrittura del campo.
La AtomicBoolean
classe ti dà un valore booleano che puoi aggiornare atomicamente. Usalo quando hai più thread che accedono a una variabile booleana.
La panoramica del pacchetto java.util.concurrent.atomic fornisce una buona descrizione di alto livello di ciò che fanno le classi in questo pacchetto e quando usarle. Consiglierei anche il libro Java Concurrency in Practice di Brian Goetz.
Estratto dalla descrizione del pacchetto
Descrizione del pacchetto java.util.concurrent.atomic: un piccolo toolkit di classi che supporta la programmazione thread-free senza blocco su singole variabili. [...]
Le specifiche di questi metodi consentono alle implementazioni di utilizzare efficienti istruzioni atomiche a livello di macchina che sono disponibili su processori contemporanei. [...]
Le istanze delle classi AtomicBoolean, AtomicInteger, AtomicLong e AtomicReference forniscono l'accesso e gli aggiornamenti a una singola variabile del tipo corrispondente. [...]
Gli effetti di memoria per gli accessi e gli aggiornamenti di atomica generalmente seguono le regole per i volatili:
- get ha gli effetti di memoria della lettura di una variabile volatile.
- set ha gli effetti di memoria della scrittura (assegnazione) di una variabile volatile.
- weakCompareAndSet legge atomicamente e scrive in modo condizionale una variabile, è ordinata rispetto ad altre operazioni di memoria su quella variabile, ma agisce altrimenti come una normale operazione di memoria non volatile.
- compareAndSet e tutte le altre operazioni di lettura e aggiornamento come getAndIncrement hanno gli effetti di memoria sia della lettura che della scrittura di variabili volatili.
volatile boolean
vsAtomicBoolean
: stackoverflow.com/questions/3786825/...