Pod spodem AtomicInteger i AtomicIntegerArray zwykle używają tych samych API niskiego poziomu do wykonywania odczytów, zapisów i innych operacji CAS. (Na przykład OpenSDK 7 używa sun.misc.Unsafe
do wykonywania operacji CAS w obu klasach.) W związku z tym AtomicInteger [] ma niewielką przewagę wydajności. Jak już zauważyłeś, użycie AtomicIntegerArray ma ma znaczące zalety pamięci.
Z praktycznego punktu widzenia użycie późniejszego uwalnia użytkownika od konieczności konstruowania wszystkich instancji AtomicInteger. Pamiętaj, że nie możesz naiwnie alokować tych leniwie z powodów współbieżności; będziesz musiał wstępnie przydzielić lub użyć bezpiecznego mechanizmu publikowania. Więc oprócz zalet pamięci, twój kod jest czystszy.
W podobnym tonie, jeśli masz kilka obiektów z członkami AtomicInteger, na przykład:
class ReadCounter {
private final String _fileName;
private final AtomicInteger _readCount;
...
}
private final Map<String, ReadCounter> _counterByName = ...;
Wtedy można osiągnąć podobną poprawę pamięci przez modelowanie zmienną składową _readCount
jako volatile int
i korzystania AtomicIntegerFieldUpdater
.
Kogo obchodzi kontrola graniczna? Jeśli spojrzysz na (widoczny) kod źródłowy dla, powiedzmy, getAndSet, to robi on obliczenia adresu dla get i compareAndSet (choć gorzej niż to, większość procesorów może wykonać get-and-set bez cas). –
tnx (nie widziałem twojego drugiego akapitu) Więc może być dobrze nie tworzyć wszystkich obiektów w tym samym czasie? (lub z pewną losową kolejnością?) – Sarmun
(Szybko edytowałem.) Tworzenie obiektów w różnych momentach nie pomoże, ponieważ GC je przestawi. Jedna z klas w java.util.concurrent posuwa się aż do dodania wielu paddingów (w najnowszych wersjach). Powinno być łatwiejsze i bardziej przejrzyste dodawanie elementów dopełniających do AtomicIntegerArray, wystarczy przesunąć w lewo. –