AtomicInteger lazySet против набора

116

В чем разница между методами lazySetи ? В документации нечего сказать :setAtomicIntegerlazySet

В конце концов устанавливается на заданное значение.

Кажется, что сохраненное значение не будет сразу установлено на желаемое значение, а вместо этого будет запланировано установить какое-то время в будущем. Но какова практическая польза от этого метода? Любой пример?

Чеок Ян Ченг
источник

Ответы:

114

Цитируется прямо из «JDK-6275329: Добавить методы lazySet в атомарные классы» :

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

Основной вариант использования - обнуление полей узлов в неблокирующих структурах данных исключительно ради предотвращения длительного хранения мусора; он применяется, когда это безвредно, если другие потоки какое-то время видят ненулевые значения, но вы хотите, чтобы структуры в конечном итоге были GCable. В таких случаях вы можете повысить производительность, избегая затрат на нулевую переменную запись. Есть несколько других вариантов использования в этом направлении для атомики, не основанной на ссылках, поэтому этот метод поддерживается во всех классах AtomicX.

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

зевать
источник
14
Может ли кто-нибудь сделать это для остальных из нас? :(
Gaurav
14
Lazy - это энергонезависимая версия (например, не гарантируется, что изменение состояния будет видимым для всех потоков, имеющих Atomic*в области видимости).
зевнул
63
чего я не понимаю, так это того, почему в javadoc это так плохо.
Фелипе
8
Я уверен, что они в конце концов дойдут до того, чтобы это изменить. Бум бум.
MMJZ,
3
для тех, кто хочет узнать больше о барьере для магазина / загрузки и почему барьер между магазином дешевле, чем барьер для загрузки магазина. Вот легкая для понимания статья об этом. Mechanical-sympathy.blogspot.com/2011/07/…
Kin Cheung
15

lazySet может использоваться для взаимодействия между потоками rmw, поскольку xchg является атомарным, что касается видимости, когда процесс потока записи изменяет местоположение строки кэша, процессор потока чтения увидит это при следующем чтении, потому что протокол согласованности кеша процессора Intel будет гарантировать LazySet работает, но строка кеша будет обновлена ​​при следующем чтении, опять же, CPU должен быть достаточно современным.

http://sc.tamu.edu/systems/eos/nehalem.pdf Для Nehalem, который является многопроцессорной платформой, процессоры имеют возможность «отслеживать» (подслушивать) адресную шину для доступа других процессоров к системной памяти и в свои внутренние тайники. Они используют эту возможность отслеживания, чтобы поддерживать согласованность своих внутренних кэшей как с системной памятью, так и с кешами других взаимосвязанных процессоров. Если через отслеживание один процессор обнаруживает, что другой процессор намеревается записать в ячейку памяти, которую он в настоящее время кэшировал в общем состоянии, процессор отслеживания аннулирует свой блок кеша, заставляя его выполнить заполнение строки кеша при следующем доступе к той же ячейке памяти. ,

oracle hotspot jdk для архитектуры x86 cpu->

lazySet == unsafe.putOrderedLong == xchg rw (инструкция asm, которая служит мягким барьером на 20 циклов на процессоре nehelem intel)

на x86 (x86_64) такой барьер намного дешевле с точки зрения производительности, чем volatile или AtomicLong getAndAdd,

В сценарии с одним производителем и одним потребителем с очередью мягкий барьер xchg может заставить строку кодов перед lazySet (sequence + 1) для потока-производителя произойти ПЕРЕД любым кодом потока-потребителя, который будет потреблять (работать) с новыми данными, конечно Потребительский поток должен будет атомарно проверить, что последовательность производителя была увеличена ровно на единицу, используя compareAndSet (sequence, sequence + 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 (Дуг Леа работает над некоторыми новыми вещами для 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) на x86 (x86_64) такой барьер намного дешевле с точки зрения производительности, чем volatile или AtomicLong getAndAdd" -> Насколько мне известно, это не так. lazySet / putOrdered - это MOV для адреса, поэтому в кулинарной книге JMM он описывается как запрет на выполнение операций на x86.
Nitsan Wakart
11

Более подробное обсуждение происхождения и полезности lazySet и лежащего в его основе putOrdered можно найти здесь: 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

Из сводки Concurrent-atomic пакета

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

Если вам интересно узнать о lazySet, вы должны дать себе и другие объяснения.

Эффекты памяти для доступа и обновления атомных объектов обычно соответствуют правилам для летучих объектов, как указано в разделе 17.4 Спецификации языка Java ™.

get имеет эффект памяти при чтении изменчивой переменной.

set имеет эффект памяти записи (назначения) изменчивой переменной.

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

weakCompareAndSet атомарно считывает и записывает переменную по условию, но не создает каких-либо упорядоченных операций , поэтому не дает никаких гарантий в отношении предыдущих или последующих операций чтения и записи любых переменных, кроме цели weakCompareAndSet.

compareAndSet и все другие операции чтения и обновления, такие как getAndIncrement, имеют эффекты памяти как чтения, так и записи изменчивых переменных.

Аджит Ганга
источник
4

Вот мое понимание, поправьте меня, если я ошибаюсь: вы можете думать о lazySet()"полу" изменчивой: это в основном энергонезависимая переменная с точки зрения чтения другими потоками, то есть значение, установленное lazySet, может быть невидимо для других потоки. Но он становится нестабильным, когда происходит другая операция записи (может быть из других потоков). Единственное влияние lazySet, которое я могу себе представить, - это compareAndSet. Поэтому, если вы используете lazySet(), get()из других потоков все еще может получить старое значение, но compareAndSet()всегда будет иметь новое значение, поскольку это операция записи.

jyluo
источник
1
не так ли compareAndSet?
Дэйв Мотен
2

Re: попытка заглушить это -

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

Это не совсем точно, но этого должно быть достаточно, чтобы вы могли принять решение между «Хорошо, мне действительно все равно» и «Хм, позвольте мне немного подумать об этом».

Пол Маклахлан
источник