Podobne

[ Pobierz całość w formacie PDF ]

tuje bezpieczeństwo inicjalizacyjne (patrz punkt 3.5.2), które umożliwia swobodny
dostęp i współdzielenie obiektów niezmiennych.
12
Technicznie możliwe jest uzyskanie obiektu niezmiennego bez wszystkich pól ustawionych na final
 przykładem jest klasa String  ale wykorzystuje to bardzo delikatne uwarunkowania wykluczające
wyścigi i wymaga dobrego zrozumienia modelu pamięci Javy. Dla ciekawskich: klasa String leniwie
wylicza skrót tekstu przy pierwszym wywołaniu metody hashCode() i buforuje ją w polu niefinalnym.
Wszystko działa poprawnie jedynie dlatego, że pole może uzyskać tylko jedną niedomyślną wartość,
która zawsze jest taka sama, bo zostaje wyliczona na podstawie niezmiennego stanu (nie próbuj tego
w domu).
13
Wielu programistów obawia się, że to podejście powoduje problemy z wydajnością. W wielu sytuacjach
są one bezpodstawne. Alokacja jest tańsza, niż może się wydawać, a obiekty niezmienne zwiększają
wydajność, bo nie potrzebują blokad czy kopiowania defensywnego. Mają ograniczony wpływ
na szybkość mechanizmu odzyskiwania pamięci.
60 Część I f& Podstawy
Nawet jeśli obiekt jest zmienny, określenie kilku jego pól jako finalnych ułatwia analizę
jego stanu, bo ograniczenie zmienności niektórych pól zmniejsza liczbę kombinacji
stanu obiektu. Obiekt, który jest w  większości niezmienny , ale ma jedno lub dwa
zmienne pola, okazuje się łatwiejszy do analizy niż obiekt z wieloma zmiennymi polami.
Dodatkowo deklaracja pola jako final informuje innych programistów, że wskazany
element nie będzie się zmieniał.
Podobnie jak zaleca się, by wszystkie pola określać jako prywatne, jeśli nie wymagają
większej widoczności [EJ Item 12], zaleca się też oznaczanie wszystkich pól nie-
zmieniających swego stanu modyfikatorem final.
3.4.2. Przykład  użycie volatile
do publikacji obiektów niezmiennych
W klasie UnsafeCachingFactorizer ze strony 36. staraliśmy się użyć dwóch obiektów
AtomicReference do zapamiętania ostatniej wartości i rozkładu, ale to podejście nie
okazywało się bezpieczne wątkowo, bo niemożliwe było jednoczesne pobranie lub
uaktualnienie obu wartości. Użycie zmiennych ulotnych również nie rozwiązałoby
problemu. Z drugiej strony obiekty niezmienne potrafią czasem zapewnić słabą formę
niepodzielności.
Serwlet wyliczający rozkład na czynniki wykonuje dwie operacje niepodzielne: aktu-
alizację bufora i warunek sprawdzający, czy bufor zawiera wartość, którą chcemy
wyliczyć. Gdy grupa powiązanych danych musi działać w sposób niepodzielny, warto
rozważyć utworzenie dla nich klasy stanu niezmiennego, na przykład OneValueCache14
z listingu 3.12.
Listing 3.12. Niezmienny obiekt przechowujący buforowaną wartość i jej rozkład
@Immutable
public class OneValueCache {
private final BigInteger lastNumber;
private final BigInteger[] lastFactors;
public OneValueCache(BigInteger i
BigInteger[] factors) {
lastNumber = i;
lastFactors = Arrays.copyOf(factors factors.length);
}
public BigInteger[] getFactors(BigInteger i) {
if (lastNumber == null || !lastNumber.equals(i))
return null;
14
Klasa OneValueCopy nie byłaby niezmienna, gdyby nie metody pobierające i wywołania copyOf().
Metoda Arrays.copyOf() pojawia się w Javie 6, ale we wcześniejszych wersjach można użyć
metody clone().
Rozdział 3. f& Współdzielenie obiektów 61
else
return Arrays.copyOf(lastFactors lastFactors.length);
}
}
Wyścig dotyczący dostępu lub aktualizacji wielu powiązanych zmiennych udaje się
wyeliminować, używając obiektu niezmiennego przechowującego wszystkie zmienne.
Ze zmiennym obiektem trzeba stosować blokady, by zapewnić niepodzielność; wykorzy-
stując niezmienny obiekt, wątek uzyskuje do niego dostęp, nie martwiąc się o inny
wątek modyfikujący jego stan. Jeśli konieczne staje się uaktualnienie zmiennych, po-
wstaje nowy obiekt stały, ale inne wątki (widzące starszą wersję) nadal mają dostęp
do spójnego stanu.
Klasa VolatileCachedFactorizer z listingu 3.13 używa klasy OneValueCache do zapamię-
tania wartości i rozkładu na czynniki. Jeśli wątek ustawia ulotne pole cache na referencję
do obiektu OneValueCache, nowe dane od razu widzą inne wątki.
Listing 3.13. Buforowanie ostatniego wyniku w referencji ulotnej dotyczącej niezmiennego obiektu
@ThreadSafe
public class VolatileCachedFactorizer implements Servlet {
private volatile OneValueCache cache = new OneValueCache(null null);
public void service(ServletRequest req ServletResponse resp) {
BigInteger i = extractFromRequest(req);
BigInteger[] factors = cache.getFactors(i);
if (factors == null) {
factors = factor(i);
cache = new OneValueCache(i factors);
}
encodeIntoResponse(resp factors);
}
}
Operacje dotyczące bufora nie interferują między sobą, bo klasa OneValueCache jest nie-
zmienna, a pole cache za każdym razem jest udostępniane tylko raz w każdej z istotnych
ścieżek. To połączenie kilku wartości powiązanych niezmiennikiem w jednym nie-
zmiennym obiekcie oraz użycie referencji typu volatile zapewnia klasie VolatileCa-
chedFactorizer bezpieczeństwo wątkowe, choć w ogóle nie stosujemy jawnych blokad.
3.5. Bezpieczna publikacja [ Pobierz całość w formacie PDF ]

  • zanotowane.pl
  • doc.pisz.pl
  • pdf.pisz.pl
  • grolux.keep.pl
  • Powered by MyScript