Ja, die private Mitgliedsvariable bar
sollte final
richtig sein? Aber in diesem Fall ist es eine atomare Operation, einfach den Wert von int
zu lesen. Ist das also technisch sicher?
// nehme an, dass unendlich viele Threads wiederholt getBar
für dieselbe Instanz von Foo
aufrufen.
BEARBEITEN:
Angenommen, dies ist der gesamte Code für die Klasse Foo
; Alle Threads mit einem Verweis auf eine Foo
-Instanz können den Wert von bar
nicht ändern (ohne dabei auf Längen wie Reflektion usw. zu setzen).
Letzte Aktualisierung: Meine erste Schlussfolgerung war zufällig, nur meine Argumentation war fehlerhaft :-( Ich habe meine Antwort überarbeitet, um sie einigermaßen kohärent zu machen, um die Spuren meiner früheren nicht zu verbergen Schnitzer.
Wie @Wyzard sagte, obwohl bar
nach der Konstruktion nicht geändert werden kann, ist Foo
immer noch nicht Thread-sicher. Das Problem ist nicht Atomizität, sondern Sichtbarkeit. Wenn Thread 1 den Wert von bar
im Konstruktor ändert (von seinem Standardwert 0), gibt es keine Garantie, wenn andere Threads den neuen Wert sehen (oder ob sie überhaupt sehen ).
So foo
sieht wie ein unveränderliches Objekt aus. Zitat aus Java Concurrency in Practice , Abschnitt 3.4:
Ein Objekt ist unveränderlich, wenn:
- Sein Zustand kann nach der Konstruktion nicht geändert werden;
- Alle seine Felder sind endgültig; und
- Es ist richtig konstruiert (die Referenz verschwindet nicht während der Konstruktion).
Foo
sieht auf 1) und 3) OK aus, aber nicht 2). Und das ist ein entscheidender Punkt, aufgrund der obigen Überlegungen. Das Deklarieren einer Variablen final
ist eine Möglichkeit, die Sichtbarkeit zwischen verschiedenen Threads sicherzustellen. Die anderen Mittel deklarieren bar
volatile
oder synchronisieren ihre Zugriffsmethode (n). Aber natürlich würde im Falle eines unveränderlichen Objekts keiner von beiden viel Sinn ergeben.
Warum garantieren final
Felder Sichtbarkeit? Antwort von Java Concurrency in Practice, Abschnitt 3.5.2:
Da unveränderliche Objekte so wichtig sind, bietet das JavaMemory-Modell eine besondere Garantie für die Initialisierungssicherheit für die gemeinsame Nutzung unveränderlicher Objekte. Wie wir gesehen haben, bedeutet das, dass eine Objektreferenz für einen anderen Thread sichtbar wird, bedeutet nicht notwendigerweise, dass der Status dieses Objekts für den konsumierenden Thread sichtbar ist. Um eine konsistente Sicht auf den Zustand des Objekts zu gewährleisten, ist eine Synchronisation erforderlich.
Unveränderbare Objekte andererseits können auch dann sicher abgerufen werden, wenn keine Synchronisation zum Veröffentlichen der Objektreferenz verwendet wird. Damit diese Garantie für die Sicherheit der Initialisierung gewährleistet ist, müssen alle Anforderungen an die Unveränderlichkeit erfüllt sein: unmodifizierbarer Zustand, alle Felder sind endgültig und die richtige Konstruktion. [...]
Unveränderbare Objekte können von jedem Thread ohne zusätzliche Synchronisierung sicher verwendet werden, selbst wenn keine Synchronisierung für deren Veröffentlichung verwendet wird.
Diese Garantie erstreckt sich auf die Werte aller letzten Felder richtig konstruierter Objekte; auf die letzten Felder kann ohne zusätzliche Synchronisation sicher zugegriffen werden. Wenn finale Felder jedoch auf veränderbare Objekte verweisen, ist weiterhin eine Synchronisierung erforderlich, um auf den Status der Objekte, auf die sie verweisen, zuzugreifen.
Und was passiert, wenn das Feld nicht final ist? Andere Threads können einen veralteten Wert des Felds automatisch anzeigen. Es gibt keine Ausnahme oder irgendeine Art von Warnung - das ist ein Grund, warum diese Art von Fehlern so schwer zu verfolgen ist.
Tags und Links java concurrency int