2017-02-05 13 views
7

Czytałem w Oracle docs że:Dlaczego AtomicInteger jest potrzebny, jeśli zapisuje i czyta do zmiennych int są atomowe?

  • odczytuje i zapisuje są atomową dla zmiennych referencyjnych i dla większości
    zmiennych pierwotnych (wszystkie typy oprócz długości i podwójnej).

(myślę, że ta funkcja została dodana w jakiejś nowej wersji JDK, ponieważ myślałem, że odczytuje/zapisuje wszystkich zmiennych pierwotnych są NIE atomowej)

Czy to znaczy, że AtomicInteger jest przestarzała i nie powinno się używać w nowych projektach?

+4

Atomowość zapisu ma niewiele wspólnego z przypadkami "AtomicInteger". Na przykład. 'compareAndSet' jest nadal bardzo ważny i nie ma nic wspólnego z atomowością jego zapisów. – luk2302

+2

Wszystkie prymitywy z wyjątkiem 'long' i' double' zawsze były atomowe dla odczytywania i zapisywania. To się nie zmieniło dla 'int'. –

+1

Co, jeśli atomowość nie jest wszystkim, czego potrzebujesz? A jeśli potrzebujesz np. Gwarancji widoczności? –

Odpowiedz

14

Podczas jednego sklepu lub pojedynczy ładunek od zwykłego int jest atomowy w Javie, nie można atomowo, powiedzmy, zwiększać ją.Wykonanie tej czynności wymagałoby najpierw załadowania wartości, a następnie obliczenia nowej wartości w zależności od niej, a następnie zapisania nowej wartości z powrotem. Ale między dwoma dostępami inny wątek mógł zmodyfikować wartość. AtomicInteger zapewnia takie operacje, jak getAndIncrement, które mogą być używane do tego celu bez konieczności użycia blokady.

7

Przestarzałe? Ani trochę. Podczas gdy indywidualne odczyty i zapisy zmiennych pierwotnych są atomowe, AtomicInteger (i inne klasy atomowe w java.util.concurrent.atomic) zapewniają bardziej złożone operacje, które są również atomowe. Należą do nich rzeczy takie jak addAndGet(int), które nie są wcale atomowe dla pierwotnych zmiennych int. Zatem

int i = 3; 
AtomicInteger j = new AtomicInteger(3); 
i += 5; // NOT thread-safe -- might not set i to 8 
int n = j.addAndGet(5); // thread-safe -- always sets n to 8 

(Oba komentarze powyżej są przy założeniu, że i i j nie są zmieniane przez czas oświadczenie w kwestii rozpoczyna wykonaniu, ale może być zmieniona przez inny wątek po rozpoczęciu wykonywania, ale przed zostało zakończone.)

5

Czy to oznacza, że ​​AtomicInteger jest przestarzałe i nie powinno być używane w nowych projektach?

Nie. Najpierw i najbardziej oczywiste, gdyby było przestarzałe, byłoby oznaczone jako takie.

Co więcej, AtomicInteger i prymitywne int po prostu nie są wymienne. Istnieje wiele różnic, ale tutaj są trzy pierwsze, które przychodzą na myśl:

  • AtomicInteger może być przekazany przez odniesienie, w przeciwieństwie do pierwotnego int, który jest przekazywany przez wartość.
  • AtomicInteger ma kilka operacji, takich jak compareAndSet(), które są niedostępne dla elementów pierwotnych.
  • Nawet te, które powierzchownie wyglądają tak samo, a mianowicie AtomicInteger.getAndIncrement() w stosunku do int++ są różne; pierwsza jest atomowa, druga to dwie operacje, które nie są razem atomowe.

Chyba ta funkcja została dodana w jakiejś nowej wersji JDK, ponieważ myślałem, że odczytuje/zapisuje wszystkich zmiennych prymitywnych NIE są atomowy

Odczytuje i zapisuje prymitywów z 32- bity lub mniejsze zawsze były atomowe.

+2

Nic w Javie nie jest "przekazywane przez odniesienie". Pomysł, który próbujesz opisać, polega na tym, że 'AtomicInteger' jest typem _reference_. Każdy parametr typu 'AtomicInteger' jest odniesieniem do jakiegoś obiektu, tak jak każda zmienna lokalna i każde pole tego typu. Jeśli mam 'AtomicInteger ai = someAtomicInteger;' i nazwałem funkcję pass-by-reference, 'foobar (ai)', to funkcja 'foobar' mogłaby zmienić' ai' na odwołanie do jakiegoś innego obiektu AtomicInteger. To nigdy nie może się zdarzyć w Javie. Java jest zawsze wywoływana według wartości.Po prostu w Javie "wartości" często są odniesieniami. –

3

W innych odpowiedziach wyjaśniono, dlaczego potrzebne jest AtomicInteger. Chciałbym wyjaśnić, o czym mówi ten dokument.

Użycie terminu atomowego w tym dokumencie nie jest takie samo, jak jego użycie w AtomicInteger.

Dokument stwierdza również

działania atomowe nie mogą być przeplatane, więc można je stosować bez obawy interferencji gwintu.

Odnosi się

int x; 
x = 1; // thread 1 
x = 2; // thread 2 
System.out.println(x); // thread 3 

thread 3 gwarantuje patrz albo wartość 1 lub wartość 2.

Jednak z long lub double, nie masz tej gwarancji. Java Language Specification stwierdza

dla celów języka programowania Java model pamięci, a pojedynczego zapisu do nieulotnej long lub double wartości jest traktowany jako dwa oddzielne pisze: jeden do każdego 32-bitowego połowę. Może to spowodować sytuację, w której wątek widzi pierwsze 32 bity 64-bitowej wartości od jeden zapis, a drugi 32 bity od innego zapisu.

Tak więc, na przykład,

long x; 
x = 0xffff_ffffL; // thread 1 
x = 0x7fff_ffff_0000_0000L; // thread 2 
System.out.println(x); // thread 3 

thread 3 może zobaczyć pierwsze 32 bity z thread 1 'przeniesienie s, a ostatnie 32 bitów z thread 2' przeniesienie s, tworząc wartość 7fff_ffff_ffff_fffflong. To samo może wystąpić dla double.

Modyfikowanie zmiennej long lub double za pomocą volatile zapobiega temu zjawisku.

Powiązane problemy