2010-12-15 18 views
9

DZone refcard zatytułowanych „Core Java Concurrency” stwierdza:Java: Ostateczne zamrożenie pole na obiekcie osiągalny z pól końcowych

Po ustawieniu wartości końcowy pole nie może być zmieniony. Oznaczenie pola odniesienia obiektu jako ostatecznego nie uniemożliwia późniejszej zamiany obiektów przywołanych z tego pola. W przypadku przykładu , końcowe pole ArrayList nie może zostać zmienione na inną ArrayList , ale obiekty mogą być dodawane lub usuwane w instancji listy.

i

Końcowy zamrażania pole zawiera nie tylko końcowe pól w obiektu, ale również wszystkie obiekty oddalone od tych ostatnich dziedzinach.

Nie do końca wiadomo o drugim stwierdzeniu. Czy to oznacza, że ​​jeśli mam ostatnie pole w klasie A typu B, które z kolei ma końcowe pole typu Integer, to ostatnie zatrzymanie pola dla instancji klasy A kończy się dopiero po ostatecznym zamrożeniu pola dla b.c stało się?

public class A{ 

    public final B b = new B(); 

} 

public class B{ 

    public final Integer c = 10; 

} 

Odpowiedz

8

Czy to oznacza, że ​​jeśli mam ostateczną pola w klasie A typ klasy B, które z kolei mają ostateczną pole typu Integer, to pole końcowe zamrażać dla instancji klasy A dopełnia dopiero po ostatecznym zamrożeniu pola dla bc już się stało?

myślę, że będzie dokładnie powiedzieć, że końcowy freeze pole w tym przypadku oznacza, że ​​podczas tworzenia instancji i bezpiecznie publikować go inne obiekty nigdy nie zobaczy niezainicjowanej wartości dla b lub c.

Powiedziałbym również, że gdy tworzysz instancję B wewnątrz A, inny kod inicjujący wewnątrz A nigdy nie zobaczy niezainicjowanej wartości dla c.

Jeden przypadek gdzie ja spotkałem prawdziwych pytań wokół ostatecznego zamrożenia pole jest na przykład klasa, która zawiera (zmienny) HashMap, przeznaczone tylko do odczytu, zainicjowany w trakcie budowy:

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); 
    } 
} 

Powstaje pytanie tutaj: zakładając, że ten obiekt jest bezpiecznie publikowany i biorąc pod uwagę, że nie ma tu żadnej synchronizacji, czy inne wątki mogą bezpiecznie wywoływać funkcję getDayName()?Odpowiedź brzmi: tak, ponieważ ostateczne zamrożenie pola gwarantuje, że HashMap i wszystko, co można z niego uzyskać (tutaj są tylko ciągi, ale mogą to być dowolnie złożone obiekty) zostaje zamrożone na końcu budowy. [Jeśli chcesz zmodyfikować tę mapę po zbudowaniu, będziesz potrzebował jawnej synchronizacji wokół czytań i zapisów.] Oto lengthier blog odkrywania tematu i sprawdzania komentarzy dla interesujących odpowiedzi takich ludzi jak Brian Goetz.

btw Jestem autorem refcard

+1

Dzięki Alex za szczegółową odpowiedź. Naprawdę doceniony. BTW Bardzo podobała mi się również karta Refcard. –

+1

>> ... ostateczne zamrożenie pola w tym przypadku oznacza, że ​​gdy tworzysz instancję A i bezpiecznie ją publikujesz, inne obiekty nigdy nie zobaczą niezainicjowanej wartości dla b lub c << - nie, właściwie "bezpieczne publikowanie" nie jest Wymagane tutaj. Jedyny wymóg: "to" nie może uciec od konstruktora. – Male

+0

@Alex Miller "Ostateczne zamrożenie pola" jest oficjalnym terminem? – nish1013

1

To naprawdę nie ma sensu mówić o tym, co staje się ostatecznym przed czym innym. Do twojego programu, gdy twój obiekt zostanie utworzony (właściwie od momentu przypisania pola), odniesienie nie może już się zmienić. Ponieważ instancja B jest tworzona przed instancją A, można powiedzieć, że c staje się ostateczna przed b, ale tak naprawdę nie ma znaczenia.

W przypadku, gdy zamówienie jest ważne, jest to, gdy masz kilka pól końcowych w jednej klasie. Jeśli chcesz użyć wartości jednego pola końcowego w przypisaniu innego, powinieneś tylko uzyskać dostęp do pól, które już zostały zainicjalizowane.

Szczerze mówiąc, to zdanie "ostateczne zamrożenie pola" nie ma dla mnie większego sensu.

4

Java Concurrency in Practice wspomina o tym w rozdziale 16.3:

Inicjowanie gwarancje bezpieczeństwa, które dla prawidłowo skonstruowane obiekty, wszystkie wątki będą wyświetlone właściwe wartości końcowych pól, które zostały ustawione przez konstruktora, niezależnie o tym, jak obiekt został opublikowany. Ponadto, dowolne zmienne, które mogą być osiągnęła przez końcowy zakresie odpowiednio zbudowanego obiektu (takie jak elementy końcowej matrycy lub z zawartość HashMap odwołuje końcowym obszarze ) są również gwarantowane być widoczny dla innych wątków. Dla obiektów z polami końcowymi, inicjalizacja bezpieczeństwo uniemożliwia ponowne zamawianie dowolnej części konstrukcji o początkowym obciążeniu odniesienia do tego obiektu.Wszystko pisze do końcowych polach dokonanych przez konstruktora , jak również do wszelkich zmiennych osiągalnych za pośrednictwem tych dziedzinach, stają się „zamrożony”, gdy konstruktor kończy, a każdy wątek że uzyskuje odwołanie do tego obiektu jest gwarantowane zobacz wartość , która jest co najmniej tak samo aktualna jak zamrożona wartość . Wpisuje, że zmienne o wartości można uzyskać za pomocą końcowych pól , które nie są ponownie zamieniane na operacje następujące po zamrożeniu po zakończeniu budowy .

+0

Co dokładnie autor oznacza "niezależnie od tego, jak obiekt jest publikowany"? Uważam, że właściwie skonstruował sam siebie, którego odniesienie nie ucieka podczas budowy. – sjain

2

Dobrze. Wynika to z JMM

Look do ust:

Obiekt jest uważana za całkowicie zainicjowana podczas jego wykończenia konstruktora. Wątek, który może tylko zobaczyć odniesienie do obiektu po tym, jak obiekt został całkowicie zainicjowany, gwarantuje poprawne zainicjowanie wartości dla ostatecznych pól tego obiektu.

Ponieważ konstruktor nie zostanie zakończone, dopóki klasa B inicjuje które gwarantują zamrożenie B.c

2

Gwarancja jest silniejsza niż wydaje się myśleć. Ostateczna semantyka pola dotyczy nawet obiektów zmiennych, które są przypisane do pól końcowych (ze zwykłymi ograniczeniami). Po prostu rozszerzam twój przykład, aby A.b był prywatny imutable (ale nie można go modyfikować zewnętrznie).

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

public class B { 
    public Integer c = 10; 
} 

W tym przypadku A.get nigdy nie powróci null nawet pod niebezpiecznym publikacji. Oczywiście ten przykład jest całkowicie abstrakcyjny i dlatego bez znaczenia. Zazwyczaj jest to ważne dla tablic (na przykład w String) i kolekcji.

+0

jeśli umieścisz swój ostatni init na końcu konstruktora i masz zły kod, który wycieknie to z konstruktora, możesz uzyskać zerowy –

Powiązane problemy