AtomicInteger lazySet vs. set


116

У чому різниця між методами lazySetта setметодами AtomicInteger? Документація не багато говорити про lazySet:

Врешті-решт встановлюється задане значення.

Здається, що збережене значення не буде відразу встановлено на потрібне значення, а замість цього буде заплановано встановити деякий час у майбутньому. Але, яке практичне використання цього методу? Будь-який приклад?

Відповіді:


114

Цитується прямо з "JDK-6275329: Додавання методів lazySet до атомних класів" :

Як, мабуть, останній маленький наступний JSR166 для Mustang, ми додали метод "lazySet" до класів Atomic (AtomicInteger, AtomicReference тощо). Це нішевий метод, який іноді корисний при точній настройці коду з використанням неблокуючих структур даних. Семантика полягає в тому, що запис гарантується, що він не буде переупорядкований з будь-яким попереднім записом, але він може бути переупорядкований наступними операціями (або, що еквівалентно, може бути не видно для інших потоків), поки не відбудеться якесь інше мінливе записування чи синхронізація.

Основний випадок використання - це видалення полів вузлів у неблокуючих структурах даних виключно задля уникнення тривалого утримання сміття; він застосовується, коли він нешкідливий, якщо інші потоки деякий час бачать ненульові значення, але ви хочете переконатися, що структури з часом GCable. У таких випадках ви можете досягти кращої продуктивності, уникаючи витрат на нульове запитання. У цьому напрямку є також кілька інших випадків використання для атомів, що не мають посилань, тому метод підтримується в усіх класах AtomicX.

Для людей, які люблять думати про ці операції з точки зору бар'єрів на машинному рівні на звичайних мультипроцесорах, lazySet забезпечує попередній бар'єр у магазині (який є неоперативним або дуже дешевим на сучасних платформах), але немає бар'єру для завантаження магазину. (що зазвичай є дорогою частиною летючого запису).


14
Може хтось придушить це для решти нас? :(
Гаурав

14
Ледачий - це енергонезалежна версія (наприклад, зміна стану не гарантується, щоб бути видимою для всіх потоків, які мають Atomic*область застосування).
позіхати

63
що я не розумію, це те, чому javadoc настільки поганий з цього приводу.
Феліпе

8
Я впевнений, що вони зрештою обійдуться його зміною. Бум-бум.
MMJZ

3
для тих, хто хоче дізнатися більше про бар'єр для зберігання / завантаження і чому бар'єр для магазину-магазину дешевший, ніж бар'єр для завантаження. Ось легка для розуміння стаття про це. механічнасимпатія.blogspot.com/2011/
Кін Ченг

15

lazySet може бути використаний для зв'язку між потоками Rmw, тому що xchg є атомним, як для видимості, коли процес потоку програми редагує місце розташування кеш-лінії, процесор потоку читача побачить це при наступному прочитанні, оскільки протокол узгодженості кешу Intel cpu гарантує LazySet працює, але рядок кешу буде оновлено при наступному прочитанні, знову ж таки, процесор повинен бути досить сучасним.

http://sc.tamu.edu/systems/eos/nehalem.pdf Для Nehalem, який є багатопроцесорною платформою, процесори мають можливість "прослуховувати" (підслуховувати) адресну шину для доступу інших процесорів до системної пам'яті та до їх внутрішніх схованок. Вони використовують цю функцію прослуховування, щоб підтримувати свої внутрішні кеші як із системною пам'яттю, так і з кешами в інших взаємопов'язаних процесорах. Якщо через прослуховування одного процесора виявиться, що інший процесор має намір записати в місце пам’яті, яке він наразі кеширував у загальному стані, процесор, який провіряє, буде недійсним блоком кешу, змушуючи його виконувати заповнення рядка кеша наступного разу, коли він отримує доступ до того самого місця пам'яті .

oracle hotspot jdk для x86 архітектури процесора->

lazySet == unsafe.putOrderedLong == xchg rw (інструкція asm, яка служить м'яким бар'єром вартістю 20 циклів на nehelem intel cpu)

на x86 (x86_64) такий бар'єр набагато дешевший, ніж мінливий або AtomicLong getAndAdd,

У сценарії одного виробника, однієї черги споживачів, xchg м'який бар'єр може змусити рядок кодів перед lazySet (послідовність + 1), щоб поток виробника відбувся ПЕРЕД будь-яким кодом споживача, який буде споживати (працювати над) новими даними, звичайно споживчий потік повинен буде атомно перевірити, чи послідовність виробника нарощена рівно на одну, використовуючи порівнянняAndSet (послідовність, послідовність + 1).

Я простежив за вихідним кодом Hotspot, щоб знайти точне відображення коду lazySet на cpp: http://hg.openjdk.java.net/jdk7/jdk7/hotspot/file/9b0ca45cd756/src/share/vm/prims/unsafe. cpp Unsafe_setOrderedLong -> SET_FIELD_VOLATILE визначення -> OrderAccess: release_store_fence. Для x86_64, OrderAccess: release_store_fence визначається як використання інструкції xchg.

Ви можете побачити, як це точно визначено в jdk7 (doug lea працює над новими новинками для JDK 8): http://hg.openjdk.java.net/jdk7/jdk7/hotspot/file/4fc084dac61e/src/os_cpu/ linux_x86 / vm / orderAccess_linux_x86.inline.hpp

ви також можете використовувати hdis для того, щоб розібрати збірку коду lazySet в дії.

Є ще одне пов'язане питання: чи потрібна нам mfence під час використання xchg


5
Важко зрозуміти, що ти тут отримуєш. Чи можете ви уточнити свою думку?
Пол Беллора

3
"lazySet == unsafe.putOrderedLong == xchg rw (інструкція asm, яка служить м'яким бар'єром, що коштує 20 циклів на nehelem intel cpu) на x86 (x86_64), такий бар'єр набагато дешевший за продуктивність, ніж мінливий або AtomicLong getAndAdd" -> Наскільки я не знаю, це вірно. lazySet / putOrряд - це MOV для адреси, саме тому кулінарна книга JMM описує це як неоперативний на x86.
Nitsan Wakart

11

Більш широку дискусію про походження та корисність lazySet та основного putOrced можна знайти тут: http://psy-lob-saw.blogspot.co.uk/2012/12/atomiclazyset-is-performance-win-for.html

Підводячи підсумок: lazySet - це слабкий непостійний запис у тому сенсі, що він виступає як магазин-магазин, а не паркан, що завантажує. Це зводиться до того, що lazySet складається з JIT до інструкції MOV, яку компілятор не може бути повторно замовлений, а не значно більш дорогою інструкцією, що використовується для летючого набору.

Читаючи значення, ви завжди закінчуєтеся з летючим читанням (у будь-якому випадку з Atomic * .get ()).

lazySet пропонує одному письменнику послідовний механізм летючого запису, тобто цілком легітимним є те, що один автор може використовувати lazySet для збільшення лічильника; декілька потоків, що збільшують один і той же лічильник, повинні вирішити конкуруючі записи за допомогою CAS, саме це і відбувається під обкладинки Atomic * для incAndGet.


точно, чому ми не можемо сказати, що це простий StoreStoreбар'єр, але не такий StoreLoad?
Євген

8

З резюме паралельно-атомного пакета

lazySet має на пам'яті ефекти запису (присвоєння) мінливої ​​змінної, за винятком того, що вона дозволяє переупорядкувати наступні (але не попередні) дії з пам'яттю, які самі по собі не накладають обмежень щодо упорядкування звичайних енергонезалежних записів. Серед інших контекстів використання, lazySet може застосовуватися під час скасування нуля задля вивезення сміття, посилання, яке більше ніколи не можна отримати.

Якщо вам цікаво lazySet, то ви також зобов'язані іншим поясненням

Ефекти пам’яті для доступу та оновлення атомів зазвичай відповідають правилам летючих речовин, як зазначено в розділі 17.4 Специфікації мови Java ™.

get має на пам'яті ефекти читання мінливої ​​змінної.

Набір має ефекти на пам'ять написання (призначення) мінливої ​​змінної.

lazySet має на пам'яті ефекти запису (присвоєння) мінливої ​​змінної, за винятком того, що вона дозволяє переупорядкувати наступні (але не попередні) дії з пам'яттю, які самі по собі не накладають обмежень щодо упорядкування звичайних енергонезалежних записів. Серед інших контекстів використання, lazySet може застосовуватися під час скасування нуля задля вивезення сміття, посилання, яке більше ніколи не можна отримати.

слабкий атомний зчитує та умовно записує змінну, але не створює жодних випадків перед замовленнями, тому не надає гарантій щодо попереднього чи наступного читання та запису будь-яких змінних, окрім цілі слабкогоCompareAndSet.

сравнениеAndSet та всі інші операції читання та оновлення, такі як getAndIncrement, мають ефекти на пам'ять як читання, так і запису змінних змінних.


4

Ось моє розуміння, виправте мене, якщо я помиляюся: ви можете думати про lazySet()"напів" мінливу: це в основному енергонезалежна змінна з точки зору читання іншими потоками, тобто значення, встановлене lazySet, може бути не видно іншим нитки. Але він стає мінливим, коли відбувається інша операція запису (може бути з інших потоків). Єдиний вплив lezySet, який я можу собі уявити, - це compareAndSet. Отже, якщо ви використовуєте lazySet(), get()з інших потоків все-таки можна отримати старе значення, але compareAndSet()завжди матиме нове значення, оскільки це операція запису.


1
ти не маєш на увазі compareAndSet?
Дейв Мотен

2

Re: спроба притупити це -

Ви можете подумати про це як про спосіб поводження з мінливим полем, як якщо б воно не було непостійним для певної операції зберігання (наприклад: ref = null;).

Це не зовсім точно, але повинно бути достатньо, щоб ви могли прийняти рішення між "Добре, мені дуже все одно" і "Хм, дозвольте мені трохи подумати над цим".

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.