Safe Publication (sichere Veröffentlichung) ist die garantierte sichere Verbreitung eines Objekts zwischen Threads: Wenn ein Thread ein Objekt erstellt und initialisiert, sieht ein anderer Thread immer das vollständig konstruierte Objekt und nicht seinen teilweise initialisierten Zustand.
Ohne sichere Veröffentlichung kann es zu einer Race Condition kommen: Ein Thread sieht einen nicht initialisierten Teil des Objekts, selbst wenn der Konstruktor bereits ausgeführt wurde.
Möglichkeiten zur Gewährleistung der sicheren Veröffentlichung:
Beispiel (unsicher):
public class Holder { private int n; public Holder(int n) { this.n = n; } } Holder holder; void publish() { holder = new Holder(42); } // Sicherheit nicht gewährleistet
Es könnten nicht vollständig konstruierte Objekte sichtbar sein!
Beispiel (sicher):
volatile Holder holder; void publish() { holder = new Holder(42); } // Das Lesen von holder wird auch sicher sein
Garantiert die Verwendung von final-Feldern in einem Objekt immer eine sichere Veröffentlichung?
Antwort: Nein, nur wenn die Referenz auf das neue Objekt vor dem Ende des Konstruktors veröffentlicht wird. Wenn die Referenz in andere Threads gelangt, bevor der Konstruktor abgeschlossen ist, können die Felder möglicherweise nicht vollständig initialisiert sein. Es sollte vermieden werden, this oder Referenzen auf nicht vollständig konstruierte Objekte während der Ausführung des Konstruktors zu veröffentlichen.
Beispiel (gefährlich!):
public class PublishEscape { public static PublishEscape instance; public PublishEscape() { instance = this; // Schlecht! this wird vor dem Ende des Konstruktors veröffentlicht } }
Geschichte
Ein Entwickler hatte einen Verweis auf ein Service-Exemplar in einem über static zugänglichen Feld zugewiesen, bevor der Konstruktor abgeschlossen war. Infolgedessen erhielt ein anderer Thread ein teilweise konstruiertes Objekt, was im Produktionsumfeld zu unvorhersehbarem Verhalten führte.
Geschichte
In einer Webanwendung wurden Singleton-Objekte ohne zusätzliche Synchronisation erstellt. Unter Last erhielten einige Threads null oder inkorrekt initialisierte Felder, was zu intermittierenden NullPointerExceptions führte.
Geschichte
In der Bibliothek wurde eine normale Liste als Cache zwischen Threads verwendet – es fehlte die sichere Veröffentlichung, die Initialisierung garantierte nicht die Sichtbarkeit für neue Threads. Infolgedessen arbeitete der Cache chaotisch und verletzte die Datenintegrität.