В качестве, вероятно, последнего небольшого продолжения JSR166 для Mustang, мы добавили метод "lazySet" к классам Atomic (AtomicInteger, AtomicReference и т. Д.). Это нишевый метод, который иногда бывает полезен при тонкой настройке кода с использованием неблокирующих структур данных. Семантика заключается в том, что запись гарантированно не будет переупорядочена при любой предыдущей записи, но может быть переупорядочена с последующими операциями (или, что эквивалентно, может быть не видна другим потокам), пока не произойдет какое-либо другое изменчивое действие записи или синхронизации).
Основной вариант использования - обнуление полей узлов в неблокирующих структурах данных исключительно ради предотвращения длительного хранения мусора; он применяется, когда это безвредно, если другие потоки какое-то время видят ненулевые значения, но вы хотите, чтобы структуры в конечном итоге были GCable. В таких случаях вы можете повысить производительность, избегая затрат на нулевую переменную запись. Есть несколько других вариантов использования в этом направлении для атомики, не основанной на ссылках, поэтому этот метод поддерживается во всех классах AtomicX.
Для людей, которым нравится думать об этих операциях с точки зрения барьеров на уровне машины на обычных мультипроцессорах, lazySet обеспечивает предшествующий барьер между магазином (который на текущих платформах либо не работает, либо очень дешев), но не препятствует загрузке магазина. (что обычно является дорогостоящей частью энергозависимой записи).
Atomic*
в области видимости).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
источник
Более подробное обсуждение происхождения и полезности 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
?Из сводки Concurrent-atomic пакета
lazySet имеет эффекты памяти записи (назначения) изменчивой переменной, за исключением того, что он разрешает переупорядочивание с последующими (но не предыдущими) действиями с памятью, которые сами по себе не накладывают ограничений переупорядочения с помощью обычных энергонезависимых записей. Среди других контекстов использования lazySet может применяться при обнулении для сбора мусора ссылки, к которой больше никогда не будет доступа.
Если вам интересно узнать о lazySet, вы должны дать себе и другие объяснения.
источник
Вот мое понимание, поправьте меня, если я ошибаюсь: вы можете думать о
lazySet()
"полу" изменчивой: это в основном энергонезависимая переменная с точки зрения чтения другими потоками, то есть значение, установленное lazySet, может быть невидимо для других потоки. Но он становится нестабильным, когда происходит другая операция записи (может быть из других потоков). Единственное влияние lazySet, которое я могу себе представить, - этоcompareAndSet
. Поэтому, если вы используетеlazySet()
,get()
из других потоков все еще может получить старое значение, ноcompareAndSet()
всегда будет иметь новое значение, поскольку это операция записи.источник
compareAndSet
?Re: попытка заглушить это -
Вы можете думать об этом как о способе обработки изменчивого поля, как если бы оно не было изменчивым для конкретной операции хранилища (например, ref = null;).
Это не совсем точно, но этого должно быть достаточно, чтобы вы могли принять решение между «Хорошо, мне действительно все равно» и «Хм, позвольте мне немного подумать об этом».
источник