Antworten:
Wenn mehrere Threads den Booleschen Wert überprüfen und ändern müssen. Beispielsweise:
if (!initialized) {
initialize();
initialized = true;
}
Dies ist nicht threadsicher. Sie können das Problem beheben, indem Sie Folgendes verwenden AtomicBoolean
:
if (atomicInitialized.compareAndSet(false, true)) {
initialize();
}
true
wenn sie initialize()
noch nicht abgeschlossen sind. Es funktioniert also nur, wenn andere Threads sich nicht um die Fertigstellung kümmern initialize()
.
initialized
es einfach verwendet wird, um sicherzustellen, dass nur ein Thread die initialize()
Methode aufruft . Offensichtlich initialized
wahr zu sein bedeutet nicht , dass die Initialisierung auf jeden Fall in diesem Fall abgeschlossen, so vielleicht ein etwas anderer Begriff wäre besser hier. Auch hier kommt es darauf an, wofür es verwendet wird.
volatile boolean
dasselbe wie AtomicBoolean
?
synchronized
Blocks. In diesem Fall benötigen Sie keinen AtomicBoolean
, nur noch einen volatile boolean
. (stellt if(! this.initialized) { synchronized(this) { if(! this.initialized) { initialize(); this.initialized = true; } } }
sicher, dass nur ein Thread aufruft initialize
und dass alle anderen Threads darauf warten, sofern dies initialized
markiert ist volatile
.)
Hier sind die Notizen (aus dem Buch von Brian Goetz ), die ich gemacht habe und die Ihnen vielleicht helfen könnten
AtomicXXX-Klassen
Bereitstellung einer nicht blockierenden Compare-And-Swap-Implementierung
Nutzt die Unterstützung durch Hardware (die CMPXCHG-Anweisung unter Intel). Wenn viele Threads durch Ihren Code laufen, der diese atomare Parallelitäts-API verwendet, werden sie viel besser skaliert als Code, der Monitore / Synchronisation auf Objektebene verwendet. Da die Synchronisationsmechanismen von Java den Code warten lassen, wenn viele Threads durch Ihre kritischen Abschnitte laufen, wird eine erhebliche Menge an CPU-Zeit für die Verwaltung des Synchronisationsmechanismus selbst aufgewendet (Warten, Benachrichtigen usw.). Da die neue API Konstrukte auf Hardwareebene (atomare Variablen) und warte- und sperrfreie Algorithmen verwendet, um die Thread-Sicherheit zu implementieren, wird viel mehr CPU-Zeit für "Aufgaben" aufgewendet als für die Verwaltung der Synchronisation.
Sie bieten nicht nur einen besseren Durchsatz, sondern auch einen größeren Widerstand gegen Lebendigkeitsprobleme wie Deadlock und Prioritätsinversion.
Es gibt zwei Hauptgründe, warum Sie einen atomaren Booleschen Wert verwenden können. Zunächst ist es veränderbar. Sie können es als Referenz übergeben und beispielsweise den Wert ändern, der dem Booleschen Wert selbst zugeordnet ist.
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
}
}
und in der someObject-Klasse
public final class SomeThreadSafeObject{
public void doSomeWork(AtomicBoolean b){
b.set(true);
}
}
Noch wichtiger ist jedoch, dass der Thread sicher ist und Entwicklern, die die Klasse verwalten, anzeigen kann, dass erwartet wird, dass diese Variable geändert und aus mehreren Threads gelesen wird. Wenn Sie keinen AtomicBoolean verwenden, müssen Sie die von Ihnen verwendete boolesche Variable synchronisieren, indem Sie sie als flüchtig deklarieren oder um das Lesen und Schreiben des Felds herum synchronisieren.
Die AtomicBoolean
Klasse gibt Ihnen einen booleschen Wert, den Sie atomar aktualisieren können. Verwenden Sie diese Option, wenn mehrere Threads auf eine boolesche Variable zugreifen.
In der Paketübersicht java.util.concurrent.atomic finden Sie eine allgemeine Beschreibung dessen, was die Klassen in diesem Paket tun und wann sie verwendet werden sollen. Ich würde auch das Buch Java Concurrency in Practice von Brian Goetz empfehlen .
Auszug aus der Paketbeschreibung
Paket java.util.concurrent.atomic Beschreibung: Ein kleines Toolkit von Klassen, die eine sperrenfreie threadsichere Programmierung für einzelne Variablen unterstützen. [...]
Die Spezifikationen dieser Methoden ermöglichen es Implementierungen, effiziente atomare Anweisungen auf Maschinenebene zu verwenden, die auf modernen Prozessoren verfügbar sind. [...]
Instanzen der Klassen AtomicBoolean, AtomicInteger, AtomicLong und AtomicReference bieten jeweils Zugriff und Aktualisierungen auf eine einzelne Variable des entsprechenden Typs. [...]
Die Memory-Effekte für Zugriffe und Aktualisierungen von Atomics folgen im Allgemeinen den Regeln für flüchtige Stoffe:
- get hat die Memory-Effekte des Lesens einer flüchtigen Variablen.
- set hat die Speichereffekte des Schreibens (Zuweisens) einer flüchtigen Variablen.
- schwachCompareAndSet liest eine Variable atomar und schreibt sie bedingt, ist in Bezug auf andere Speicheroperationen für diese Variable geordnet, wirkt aber ansonsten wie eine gewöhnliche nichtflüchtige Speicheroperation.
- compareAndSet und alle anderen Lese- und Aktualisierungsvorgänge wie getAndIncrement haben die Speichereffekte beim Lesen und Schreiben flüchtiger Variablen.
volatile boolean
vsAtomicBoolean
: stackoverflow.com/questions/3786825/…