Mam kawałek kodu, który wygląda tak:Czy zmienne zmienne Java narzucają relację, która miała miejsce przed odczytaniem?
Fragment A:
class Creature {
private static long numCreated;
public Creature() {
synchronized (Creature.class) {
numCreated++;
}
}
public static long numCreated() {
return numCreated;
}
}
Z mojego zrozumienia, ponieważ odczyt numCreated
nie jest zsynchronizowany, jeśli wątek-A tworzy Creature
na 1pm , a Thread-B czyta numCreated()
o godzinie 14.00, numCreated()
może również powrócić 0 lub 1 (nawet gdy wątek-A zakończył inicjowanie obiektu o godzinie 13:15).
Więc dodałem synchronized
do numCreated()
:
Fragment B:
class Creature {
private static long numCreated;
public Creature() {
synchronized (Creature.class) {
numCreated++;
}
}
public static synchronized long numCreated() { // add "synchronized"
return numCreated;
}
}
i wszystko jest dobrze, poza tym, że myślałem, gdybym go zmodyfikować, aby Snippet C jest zmienna numCreated
nadal poprawnie synchronizowane?
Fragment C:
class Creature {
private static volatile long numCreated; // add "volatile"
public Creature() {
synchronized (Creature.class) {
numCreated++;
}
}
public static long numCreated() { // remove "synchronized"
return numCreated;
}
}
Z Snippet C, jest to gwarancją, że jak tylko wątek-A kończy tworzenie obiektów w 1:05 pm, call thread-B do numCreated()
pewnością powrót 1
?
PS: Rozumiem, że w realnej sytuacji to pewnie użyć AtomicLong
ale to jest dla celów uczenia
Lotne odczyty są tanie, to jest to, co powinieneś wiedzieć. Jednak nie są tanie. Synchronizacja wymaga zarówno zmiennego zapisu, jak i CAS (porównaj i ustaw), a także monitorowanych poręczycieli dla OS, co jest motywacją do zablokowania impl. – bestsss
biorąc pod uwagę poprzedni komentarz, nie próbuj grać * inteligentniej * przez usunięcie volatile. – bestsss
@bestsss Nie rozumiem twojego ostatniego komentarza, co masz na myśli, nie usuwając volatile? (w Snippet C dodawałem niestabilności i usuwam zsynchronizowane) – Pacerier