Jako prawdopodobnie ostatnia kontynuacja JSR166 dla Mustanga, dodaliśmy metodę „lazySet” do klas Atomic (AtomicInteger, AtomicReference itp.). Jest to niszowa metoda, która jest czasami przydatna podczas dostrajania kodu przy użyciu nieblokujących struktur danych. Semantyka polega na tym, że zapis nie zostanie ponownie uporządkowany z żadnym poprzednim zapisem, ale może zostać zmieniony w kolejnych operacjach (lub, równoważnie, może nie być widoczny dla innych wątków), dopóki nie nastąpi inna nietrwała akcja zapisu lub synchronizacji).
Głównym przypadkiem użycia jest zerowanie pól węzłów w nieblokujących strukturach danych wyłącznie w celu uniknięcia długoterminowego przechowywania elementów bezużytecznych; ma zastosowanie, gdy jest nieszkodliwe, jeśli inne wątki widzą przez jakiś czas wartości inne niż null, ale chcesz się upewnić, że ostatecznie struktury będą GCable. W takich przypadkach można uzyskać lepszą wydajność, unikając kosztów zerowego zapisu ulotnego. Istnieje kilka innych przypadków użycia wzdłuż tych linii dla atomów nie opartych na referencjach, więc metoda jest obsługiwana we wszystkich klasach AtomicX.
Dla osób, które lubią myśleć o tych operacjach w kategoriach barier na poziomie maszyny na zwykłych wieloprocesorach, lazySet zapewnia poprzednią barierę sklep-sklep (która jest albo bez operacji lub bardzo tania na obecnych platformach), ale nie ma bariery dla obciążenia sklepu (co jest zwykle kosztowną częścią zapisu ulotnego).
Atomic*
zakres).lazySet może być używany do komunikacji między wątkami rmw, ponieważ xchg jest atomowy, jeśli chodzi o widoczność, gdy proces wątku piszącego modyfikuje lokalizację linii pamięci podręcznej, procesor wątku czytnika zobaczy to przy następnym odczycie, ponieważ protokół spójności pamięci podręcznej procesora Intel zagwarantuje LazySet działa, ale linia pamięci podręcznej zostanie zaktualizowana przy następnym odczycie, ponownie procesor musi być wystarczająco nowoczesny.
http://sc.tamu.edu/systems/eos/nehalem.pdf W przypadku Nehalem, który jest platformą wieloprocesorową, procesory mają możliwość „podsłuchiwania” magistrali adresowej w celu uzyskania dostępu innych procesorów do pamięci systemowej i do ich wewnętrznych pamięci podręcznych. Korzystają z tej możliwości szpiegowania, aby zachować zgodność swoich wewnętrznych pamięci podręcznych zarówno z pamięcią systemową, jak iz pamięcią podręczną innych połączonych ze sobą procesorów. Jeśli podczas podsłuchiwania jeden procesor wykryje, że inny procesor zamierza zapisywać dane w lokalizacji pamięci, która jest obecnie przechowywana w pamięci podręcznej w stanie współdzielonym, procesor szpiegujący unieważni swój blok pamięci podręcznej, zmuszając go do wypełnienia linii pamięci podręcznej przy następnym dostępie do tej samej lokalizacji pamięci .
oracle hotspot jdk dla architektury procesorów x86->
lazySet == unsafe.putOrderedLong == xchg rw (instrukcja asm, która służy jako miękka bariera, kosztująca 20 cykli na procesorze nehelem intel)
na x86 (x86_64) taka bariera jest znacznie tańsza pod względem wydajności niż lotna czy AtomicLong getAndAdd,
W scenariuszu z jednym producentem i jednym konsumentem w kolejce, miękka bariera xchg może wymusić wiersz kodów przed lazySet (sekwencja + 1) dla wątku producenta PRZED jakimkolwiek kodem wątku konsumenckiego, który będzie oczywiście zużywał (pracował) nowe dane Wątek konsumenta będzie musiał niepodzielnie sprawdzić, czy sekwencja producenta została zwiększona o dokładnie jeden przy użyciu funkcji compareAndSet (sekwencja, sekwencja + 1).
Prześledziłem po kodzie źródłowym Hotspot, aby znaleźć dokładne mapowanie lazySet do kodu cpp: http://hg.openjdk.java.net/jdk7/jdk7/hotspot/file/9b0ca45cd756/src/share/vm/prims/unsafe. cpp Unsafe_setOrderedLong -> SET_FIELD_VOLATILE definicja -> OrderAccess: release_store_fence. W przypadku x86_64 OrderAccess: release_store_fence jest zdefiniowany przy użyciu instrukcji xchg.
Możesz zobaczyć, jak to jest dokładnie zdefiniowane w jdk7 (doug lea pracuje nad nowymi rzeczami dla JDK 8): http://hg.openjdk.java.net/jdk7/jdk7/hotspot/file/4fc084dac61e/src/os_cpu/ linux_x86 / vm / orderAccess_linux_x86.inline.hpp
możesz również użyć hdis do dezasemblacji kodu lazySet w akcji.
Jest jeszcze jedno powiązane pytanie: czy potrzebujemy mfence podczas korzystania z xchg
źródło
Szerszą dyskusję na temat pochodzenia i użyteczności lazySet i bazowego putOrdered można znaleźć tutaj: http://psy-lob-saw.blogspot.co.uk/2012/12/atomiclazyset-is-performance-win-for.html
Podsumowując: lazySet jest słabym, zmiennym zapisem w tym sensie, że działa jako sklep, a nie jako ogrodzenie sklepu. Sprowadza się to do tego, że lazySet jest skompilowany w JIT do instrukcji MOV, której kompilator nie może zmienić kolejności, a nie do znacznie droższej instrukcji używanej dla zbioru nietrwałego.
Podczas odczytywania wartości zawsze kończy się to nietrwałym odczytem (w każdym przypadku z Atomic * .get ()).
lazySet oferuje jednemu piszącemu spójny, zmienny mechanizm zapisu, tj. jest całkowicie uzasadnione, aby pojedynczy pisarz używał lazySet do zwiększania licznika, wiele wątków zwiększających ten sam licznik będzie musiało rozwiązać konkurencyjne zapisy przy użyciu CAS, co jest dokładnie tym, co dzieje się w okładki Atomic * for incAndGet.
źródło
StoreStore
bariera, ale nieStoreLoad
?Z podsumowania pakietu Concurrent-atomic
lazySet ma efekty pamięciowe pisania (przypisywania) zmiennej nietrwałej, z wyjątkiem tego, że pozwala na zmianę kolejności z kolejnymi (ale nie poprzednimi) działaniami pamięci, które same nie nakładają ograniczeń zmiany kolejności przy zwykłych zapisach nieulotnych. Wśród innych kontekstów użycia lazySet może mieć zastosowanie podczas zerowania, w celu wyrzucania elementów bezużytecznych, odwołania, do którego nigdy więcej nie uzyskano dostępu.
Jeśli ciekawi Cię lazySet, to jesteś winien również inne wyjaśnienia
źródło
Oto moje zrozumienie, popraw mnie, jeśli się mylę: Możesz myśleć o
lazySet()
"pół" niestabilnym: jest to w zasadzie nieulotna zmienna w sensie czytania przez inne wątki, tj. Wartość ustawiona przez lazySet może być niewidoczna dla innych wątki. Ale staje się niestabilny, gdy występuje inna operacja zapisu (może pochodzić z innych wątków). Jedyny wpływ lazySet, jaki mogę sobie wyobrazić, tocompareAndSet
. Więc jeśli używaszlazySet()
,get()
z innych wątków może nadal pobierać starą wartość, alecompareAndSet()
zawsze będzie miała nową wartość, ponieważ jest to operacja zapisu.źródło
compareAndSet
?Re: spróbuj go stłumić -
Można o tym myśleć jako o sposobie traktowania zmiennego pola tak, jakby nie było ulotne dla określonej operacji magazynu (np .: ref = null;).
To nie jest do końca dokładne, ale powinno wystarczyć, abyś mógł zdecydować między „OK, naprawdę mnie to nie obchodzi” a „Hmm, pozwól, że pomyślę o tym przez chwilę”.
źródło