Cześć, nie mogę ogarnąć jednej rzeczy z w/w książki. Pytanie raczej do tych co czytali.
Podrozdziały od 3.4.1 do 3.5.2 włącznie.
Jest sobie przykład mówiący, że jeśli niezmienniki zamkniemy w niemutowalnej klasie to można potem tego bezpiecznie użyć bez synchronizacji:
class OneValueCache {
private final BigInteger lastNumber;
private final BigInteger[] lastFactors;
public OneValueCache(BigInteger lastNumber, BigInteger[] lastFactors) {
this.lastNumber = lastNumber;
this.lastFactors = Arrays.copyOf(lastFactors, lastFactors.length);
}
public BigInteger[] getFactors(BigInteger i) {
if (lastNumber == null || !lastNumber.equals(i)) {
return null;
}
return Arrays.copyOf(lastFactors, lastFactors.length);
}
}
class VolatileCachedFactorized 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);
}
}
Na pewno client nie zobaczy niespójnego stanu, ale mimo wszystko mamy tutaj check-then-act
no nie? Jest warunek if na factors
a potem przypisanie nowej referencji do cache
. Teoretycznie nie ma to żadnych konsekwencji w sumie.
Zastanawiam się jednak czy dobrze rozumiem to volatile
tutaj. Jak rozumiem jest ono użyte po to, aby wątki widziały jak najnowszą referencję tutaj tak? Bez tego jakiś wątek mógłby nie widzieć najnowszego nadpisania. W tym przypadku akurat też za dużo to nie rozwali co nie?
No właśnie .. a potem jest przykład z nieprawidłową publikacją:
class Holder {
private int n;
public Holder(int n) {
this.n = n;
}
public void assertSanity() {
if (n != n) {
throw new AssertionError("This statement is false");
}
}
}
class Foo {
public Holder holder;
public void init() {
holder = new Holder(42);
}
}
No i tutaj jest jasne, że po 1 jest mutowalne, po 2 może nie widzieć najświeższej referencji do holdera albo widzieć go w złym stanie.
Jednak potem jest informacja, że gdyby Holder był niemutowalny, to można go użyć bez żadnej synchronizacji.
Czyli jak rozumiem jeśli jakaś zmienna w klasie jest finalna to nie może dojść sytuacji, że wątek zostanie wywłaszczony pomiędzy super-konstruktorem Object
, a tym konkretnym? I to jest już sprawa JMM? Kiedy mam pewność, że mój konstruktor wykona się na 100% w całości? Kiedy wszystkie pola mam finalne czy wystarczy tylko 1?
I ostatnia sprawa związana już konkretnie z tą publikacją niemutowalnego obiektu. W końcu referencja na niemutowalny obiekt musi być volatile czy nie? Bo w przykładzie referencja na OneCacheValue
jest volatile, ale potem jest napisane:
Immutable objects can be used safely by any thread without additional synchronization, even when synchronization is not used to publish them
.
To jak to w końcu o_O?