2010-12-15 10 views
9

DZone den Titel refcard „Core Java Concurrency“ heißt es:Java: Endgültiges Feld Einfrieren Objekt erreichbar von endgültigen Feldern

Einmal eingestellt, letzter Feldwert nicht geändert werden kann. Wenn Sie ein Objektverweisfeld als endgültig markieren, verhindert das nicht, dass Objekte, die von diesem Feld referenziert werden, später geändert werden. Für Beispiel kann ein abschließendes ArrayList-Feld nicht in eine andere ArrayList geändert werden, aber Objekte können in der Listeninstanz hinzugefügt oder entfernt werden.

und

Schluss Feld Einfrieren umfasst nicht nur die letzten Felder im Objekt, sondern auch alle Objekte erreichbar von diesen letzten Felder aus.

Ich bin nicht ganz klar über die zweite Aussage. Bedeutet das, dass, wenn ich ein letztes Feld in Klasse A vom Typ Klasse B habe, die wiederum ein Endfeld vom Typ Integer haben, dann wird das endgültige Einfrieren eines Feldes für eine Instanz der Klasse A erst nach dem letzten Einfrieren des Feldes für b.c abgeschlossen passiert?

Antwort

8

Bedeutet dies, dass, wenn ich ein endgültiges Feld in der Klasse A vom Typ der Klasse B habe, die wiederum eine endgültige Feld Typen Integer, dann letztes Feld für eine Instanz Einfrieren der Klasse A abgeschlossen nur nach dem letzten Einfrieren des Feldes für bc sind schon passiert?

Ich glaube, ich vorsichtig, dass in diesem Fall letztes Feld Einfrieren bedeutet sagen würde, dass, wenn Sie eine Instanz von A erstellen und sicher veröffentlichen es, anderen Objekten niemals einen nicht initialisierten Wert für b oder c sehen.

Ich würde auch sagen, dass, wenn Sie die Instanz von B in A erstellen, anderen Initialisierungscode in A nie einen nicht initialisierten Wert für c.

Ein Fall, in dem ich wirkliche Fragen rund um freeze letzten Feld angetroffen habe, ist zum Beispiel einer Klasse, die einen (änderbaren) HashMap enthält, die nur für Lese, während des Bau initialisiert:

public class DaysOfWeek { 
    private final Map daysOfWeek = new HashMap(); 
    public DaysOfWeek() { 
     // prepopulate my map 
     daysOfWeek.put(0, "Sunday"); 
     daysOfWeek.put(1, "Monday"); 
     // etc 
    } 

    public String getDayName(int dayOfWeek) { 
     return daysOfWeek(dayOfWeek); 
    } 
} 

Die Frage ist hier entsteht: unter der Annahme, dass dieses Objekt sicher veröffentlicht wird, und da es keine Synchronisation gibt, ist es für andere Threads sicher, getDayName() aufzurufen?Die Antwort ist ja, denn das finale Field Freeze garantiert, dass die HashMap und alles, was von ihr erreichbar ist (hier sind es nur Strings, aber beliebig komplexe Objekte), am Ende der Konstruktion eingefroren wird. [Wenn Sie diese Karte nach der Konstruktion tatsächlich modifizieren möchten, benötigen Sie eine explizite Synchronisation um Lese- und Schreibvorgänge.] Hier ist eine lengthier blog Erkundung des Themas und überprüfen Sie die Kommentare für einige interessante Antworten von Leuten wie Brian Goetz.

btw ich bin der Autor des refcard

+1

Dank Alex für die detaillierte Antwort. Sehr geschätzt. Übrigens, ich mochte die Refcard auch sehr. –

+1

>> ... letzte Einfrieren in diesem Fall bedeutet, dass wenn Sie eine Instanz von A erstellen und sicher veröffentlichen, andere Objekte nie einen nicht initialisierten Wert für b oder c sehen << - nein, tatsächlich "sicher veröffentlichen" ist nicht hier erforderlich. Die einzige Anforderung: 'Dies' darf nicht aus dem Konstruktor entkommen – Male

+0

@Alex Miller "Final field freeze" ist ein offizieller Begriff? – nish1013

1

Es macht nicht wirklich Sinn, darüber zu sprechen, was vor was sonst endgültig wird. Sobald das Objekt erstellt wurde (dh ab dem Zeitpunkt, an dem das Feld einmal zugewiesen wurde), kann die Referenz zu Ihrem Programm nicht mehr geändert werden. Da die B-Instanz vor der A-Instanz erstellt wird, könnte man sagen, dass c vor b zu einem Finale wird, aber das spielt keine Rolle.

Wo die Reihenfolge wichtig ist, wenn Sie mehrere letzte Felder in einer einzelnen Klasse haben. Wenn Sie den Wert eines letzten Feldes bei der Zuweisung eines anderen verwenden möchten, sollten Sie nur auf Felder zugreifen, die bereits initialisiert wurden.

Um ehrlich zu sein, macht mir dieser "final field freeze" -Satz wenig Sinn.

4

Java Concurrency in Practice erwähnt dies in Abschnitt 16.3:

Initialisierung Sicherheit garantiert, dass für richtig konstruiert Objekte, alle Themen werden die richtigen Werte der endgültigen Felder sehen, die vom Konstruktor festgelegt wurden, und zwar unabhängig davon, wie das Objekt veröffentlicht wird. Weiterhin wird keine Variablen, die durch eine letzte Feld eines richtig erreicht werden kann konstruiertes Objekt (wie die Elemente einer endgültigen Anordnung oder der Inhalt eines HashMap durch einen Endfeld referenzierten) sind ebenfalls garantiert sei sichtbar für andere Themen. Für die Objekte mit den Endfeldern, die Initialisierung Sicherheit verbietet die Neubestellung irgendeines Teils der Konstruktion mit der Anfangslast eines Verweises auf dieses Objekt.Alle schreibt letzte Felder vom Konstruktor gemacht sowie auf alle Variablen erreichbar durch diese Felder, werden „eingefroren“, wenn der Konstruktor abgeschlossen ist, und jeder Thread , die einen Verweis auf das Objekt erhält ist garantiert sehen Sie einen Wert , der mindestens so aktuell ist wie der eingefrorene Wert . Schreibvorgänge, die Variablen initialisieren, die über abschließende -Felder erreichbar sind, werden nicht mit Operationen nach dem Post-Construction-Freeze neu geordnet.

+0

Was genau Autor gemeint mit „unabhängig davon, wie das Objekt veröffentlicht“? Ich glaube, dass richtig konstruiert selbst bedeutet, wessen Hinweis während des Aufbaus nicht entgeht. – sjain

2

Rechts. Das ergibt sich aus JMM

Suchen Sie nach Absatz:

Ein Objekt betrachtet wird vollständig, wenn seine Konstruktor beendet initialisiert werden. Ein Thread, der nur einen Verweis auf ein Objekt sehen kann, nachdem das Objekt vollständig initialisiert wurde, sieht garantiert die korrekt initialisierten Werte für die letzten Felder dieses Objekts.

Da Konstruktor wird nicht beendet, bis der Klasse B, dass die Garantie Einfrieren von B.c initialisiert

2

Die Garantie ist stärker als Sie denken erscheinen. Die abschließende Feldsemantik gilt auch für änderbare Objekte, die (mit den üblichen Einschränkungen) endgültigen Feldern zugeordnet sind. SO erweitert Ihr Beispiel zu A.b private und B veränderbar (aber nicht extern veränderbar).

public class A { 
    private final B b = new B(); 
    public Integer get() { return b.c; } 
} 

public class B { 
    public Integer c = 10; 
} 

In diesem Fall wird A.get nie null auch unter unsicheren Veröffentlichung zurück. Natürlich ist dieses Beispiel völlig abstrakt und daher bedeutungslos. In der Regel ist es wichtig für Arrays (zum Beispiel in String) und Sammlungen.

+0

Wenn Sie Ihre endgültige init am Ende des Konstruktors und haben einen schlechten Code, der dies aus dem Konstruktor, dann können Sie Null –

Verwandte Themen