Java AtomicInteger lazySet 与 set

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1468007/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 12:37:35  来源:igfitidea点击:

AtomicInteger lazySet vs. set

javaconcurrencyatomic

提问by Cheok Yan Cheng

What is the difference between the lazySetand setmethods of AtomicInteger? The documentationdoesn't have much to say about lazySet:

是什么之间的区别lazySetset方法AtomicInteger?该文件并没有太多的话要说lazySet

Eventually sets to the given value.

最终设置为给定值。

It seems that the stored value will not be immediately set to the desired value but will instead be scheduled to be set some time in the future. But, what is the practical use of this method? Any example?

似乎存储的值不会立即设置为所需的值,而是会安排在将来的某个时间设置。但是,这种方法的实际用途是什么?有什么例子吗?

采纳答案by yawn

Cited straight from "JDK-6275329: Add lazySet methods to atomic classes":

直接引用自“JDK-6275329:向原子类添加 lazySet 方法”

As probably the last little JSR166 follow-up for Mustang, we added a "lazySet" method to the Atomic classes (AtomicInteger, AtomicReference, etc). This is a niche method that is sometimes useful when fine-tuning code using non-blocking data structures. The semantics are that the write is guaranteed not to be re-ordered with any previous write, but may be reordered with subsequent operations (or equivalently, might not be visible to other threads) until some other volatile write or synchronizing action occurs).

The main use case is for nulling out fields of nodes in non-blocking data structures solely for the sake of avoiding long-term garbage retention; it applies when it is harmless if other threads see non-null values for a while, but you'd like to ensure that structures are eventually GCable. In such cases, you can get better performance by avoiding the costs of the null volatile-write. There are a few other use cases along these lines for non-reference-based atomics as well, so the method is supported across all of the AtomicX classes.

For people who like to think of these operations in terms of machine-level barriers on common multiprocessors, lazySet provides a preceeding store-store barrier (which is either a no-op or very cheap on current platforms), but no store-load barrier (which is usually the expensive part of a volatile-write).

作为 Mustang 的最后一个小 JSR166 后续行动,我们向 Atomic 类(AtomicInteger、AtomicReference 等)添加了一个“lazySet”方法。这是一种利基方法,有时在使用非阻塞数据结构微调代码时很有用。语义是保证写入不会与任何先前的写入重新排序,但可以与后续操作重新排序(或等效地,可能对其他线程不可见),直到发生其他一些易失性写入或同步操作)。

主要用例是为了避免长期垃圾保留而将非阻塞数据结构中的节点字段清零;它适用于如果其他线程在一段时间内看到非空值是无害的,但您希望确保结构最终是可 GCable 的。在这种情况下,您可以通过避免 null volatile-write 的成本来获得更好的性能。对于非基于引用的原子,还有一些其他用例,因此所有 AtomicX 类都支持该方法。

对于喜欢在常见多处理器上的机器级障碍方面考虑这些操作的人,lazySet 提供了一个前面的存储-存储障碍(在当前平台上要么是空操作要么非常便宜),但没有存储-加载障碍(这通常是 volatile-write 的昂贵部分)。

回答by Paul Mclachlan

Re: attempt to dumb it down -

回复:尝试将其降低-

You can think of this as a way to treat a volatile field as if it wasn't volatile for a particular store (eg: ref = null;) operation.

您可以将其视为一种将 volatile 字段视为对于特定存储(例如:ref = null;)操作不是 volatile 的方法。

That isn't perfectly accurate, but it should be enough that you could make a decision between "OK, I really don't care" and "Hmm, let me think about that for a bit".

这并不完全准确,但应该足以让您在“好吧,我真的不在乎”和“嗯,让我考虑一下”之间做出决定。

回答by jyluo

Here is my understanding, correct me if I am wrong: You can think about lazySet()as "semi" volatile: it's basically a non-volatile variable in terms of reading by other threads, i.e. the value set by lazySet may not be visible to to other threads. But it becomes volatile when another write operation occurs (may be from other threads). The only impact of lazySet I can imagine is compareAndSet. So if you use lazySet(), get()from other threads may still get the old value, but compareAndSet()will always have the new value since it is a write operation.

这是我的理解,如果我错了,请纠正我:您可以将其lazySet()视为“半”易失性:就其他线程的读取而言,它基本上是一个非易失性变量,即由 lazySet 设置的值可能对其他人不可见线程。但是当另一个写操作发生时它变得不稳定(可能来自其他线程)。我能想象到的 lazySet 的唯一影响是compareAndSet. 因此,如果您使用lazySet(),get()来自其他线程的可能仍会获得旧值,但compareAndSet()由于它是写操作,因此将始终具有新值。

回答by Nitsan Wakart

A wider discussion of the origins and utility of lazySet and the underlying putOrdered can be found here: http://psy-lob-saw.blogspot.co.uk/2012/12/atomiclazyset-is-performance-win-for.html

可以在此处找到关于 lazySet 的起源和效用以及底层 putOrdered 的更广泛讨论:http://psy-lob-saw.blogspot.co.uk/2012/12/atomiclazyset-is-performance-win-for.html

To summarize: lazySet is a weak volatile write in the sense that it acts as a store-store and not a store-load fence. This boils down to lazySet being JIT compiled to a MOV instruction that cannot be re-ordered by the compiler rather then the significantly more expensive instruction used for a volatile set.

总结一下:从某种意义上说,lazySet 是一种弱易失性写入,它充当存储-存储而不是存储-加载栅栏。这归结为 lazySet 被 JIT 编译为编译器无法重新排序的 MOV 指令,而不是用于易失性集的更昂贵的指令。

When reading the value you always end up doing a volatile read(with an Atomic*.get() in any case).

在读取值时,您最终总是会进行易失性读取(在任何情况下都使用 Atomic*.get())。

lazySet offers a single writer a consistent volatile write mechanism, i.e. it is perfectly legitimate for a single writer to use lazySet to increment a counter, multiple threads incrementing the same counter will have to resolve the competing writes using CAS, which is exactly what happens under the covers of Atomic* for incAndGet.

lazySet 为单个写入器提供了一致的易失性写入机制,即单个写入器使用 lazySet 来增加计数器是完全合法的,增加相同计数器的多个线程将不得不使用 CAS 解决竞争写入,这正是在incAndGet 的 Atomic* 封面。

回答by porkchop

lazySet can be used for rmw inter thread communication, because xchg is atomic, as for visibility, when writer thread process modify a cache line location, reader thread's processor will see it at the next read, because the cache coherence protocol of intel cpu will garantee LazySet works, but the cache line will be updated at the next read, again, the CPU has to be modern enough.

lazySet可以用于rmw线程间通信,因为xchg是原子的,至于可见性,当写线程进程修改缓存行位置时,读线程的处理器会在下次读取时看到它,因为intel cpu的缓存一致性协议会保证LazySet 可以工作,但缓存行将在下次读取时更新,同样,CPU 必须足够现代。

http://sc.tamu.edu/systems/eos/nehalem.pdfFor Nehalem which is a multi-processor platform, the processors have the ability to “snoop” (eavesdrop) the address bus for other processor's accesses to system memory and to their internal caches. They use this snooping ability to keep their internal caches consistent both with system memory and with the caches in other interconnected processors. If through snooping one processor detects that another processor intends to write to a memory location that it currently has cached in Shared state, the snooping processor will invalidate its cache block forcing it to perform a cache line fill the next time it accesses the same memory location.

http://sc.tamu.edu/systems/eos/nehalem.pdf对于 Nehalem 是一个多处理器平台,处理器具有“窥探”(窃听)地址总线的能力,以便其他处理器访问系统内存和到他们的内部缓存。他们使用这种窥探能力来保持其内部缓存与系统内存和其他互连处理器中的缓存一致。如果通过监听一个处理器检测到另一个处理器打算写入它当前缓存在共享状态的内存位置,监听处理器将使其缓存块无效,迫使它在下次访问同一内存位置时执行缓存行填充.

oracle hotspot jdk for x86 cpu architecture->

适用于 x86 cpu 架构的 oracle hotspot jdk->

lazySet == unsafe.putOrderedLong == xchg rw( asm instruction that serve as a soft barrier costing 20 cycles on nehelem intel cpu)

lazySet == unsafe.putOrderedLong == xchg rw(作为软屏障的 asm 指令在 nehelem intel cpu 上花费 20 个周期)

on x86 (x86_64) such a barrier is much cheaper performance-wise than volatile or AtomicLong getAndAdd ,

在 x86 (x86_64) 上,这样的屏障在性能方面比 volatile 或 AtomicLong getAndAdd 便宜得多,

In an one producer, one consumer queue scenario, xchg soft barrier can force the line of codes before the lazySet(sequence+1) for producer thread to happen BEFORE any consumer thread code that will consume (work on) the new data, of course consumer thread will need to check atomically that producer sequence was incremented by exactly one using a compareAndSet (sequence, sequence + 1).

在一个生产者,一个消费者队列的场景中,xchg 软屏障可以强制生产者线程的 lazySet(sequence+1) 之前的代码行发生在任何消费者线程代码之前,这些代码将消耗(处理)新数据,当然消费者线程将需要使用 compareAndSet (sequence, sequence + 1) 原子地检查生产者序列是否增加了 1。

I traced after Hotspot source code to find the exact mapping of the lazySet to cpp code: http://hg.openjdk.java.net/jdk7/jdk7/hotspot/file/9b0ca45cd756/src/share/vm/prims/unsafe.cppUnsafe_setOrderedLong -> SET_FIELD_VOLATILE definition -> OrderAccess:release_store_fence. For x86_64, OrderAccess:release_store_fence is defined as using the xchg instruction.

我跟踪 Hotspot 源代码以找到 lazySet 到 cpp 代码的确切映射:http://hg.openjdk.java.net/jdk7/jdk7/hotspot/file/9b0ca45cd756/src/share/vm/prims/unsafe 。 cppUnsafe_setOrderedLong -> SET_FIELD_VOLATILE 定义 -> OrderAccess:release_store_fence。对于 x86_64,OrderAccess:release_store_fence 定义为使用 xchg 指令。

You can see how it is exactly defined in jdk7 (doug lea is working on some new stuff for JDK 8): http://hg.openjdk.java.net/jdk7/jdk7/hotspot/file/4fc084dac61e/src/os_cpu/linux_x86/vm/orderAccess_linux_x86.inline.hpp

您可以看到它是如何在 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

you can also use the hdis to disassemble the lazySet code's assembly in action.

您还可以使用 hdis 来反汇编 lazySet 代码的程序集。

There is another related question: Do we need mfence when using xchg

还有一个相关的问题: 我们在使用 xchg 时需要 mfence

回答by Ajeet Ganga

From the Concurrent-atomic package summary

来自Concurrent-atomic 包摘要

lazySethas the memory effects of writing (assigning) a volatile variable except that it permits reorderings with subsequent (but not previous) memory actions that do not themselves impose reordering constraints with ordinary non-volatile writes. Among other usage contexts, lazySet may apply when nulling out, for the sake of garbage collection, a reference that is never accessed again.

lazySet具有写入(分配)易失性变量的内存效果,但它允许对后续(但不是先前)内存操作进行重新排序,这些操作本身不会对普通非易失性写入施加重新排序约束。在其他使用上下文中,lazySet 可能会在清空时应用,为了垃圾收集,一个永远不会再次访问的引用。

If you are curious about lazySet then you owe yourself other explanations too

如果您对lazySet 感到好奇,那么您也欠自己其他解释

The memory effects for accesses and updates of atomics generally follow the rules for volatiles, as stated in section 17.4 of The Java? Language Specification.

gethas the memory effects of reading a volatile variable.

sethas the memory effects of writing (assigning) a volatile variable.

lazySethas the memory effects of writing (assigning) a volatile variable except that it permits reorderings with subsequent (but not previous) memory actions that do not themselves impose reordering constraints with ordinary non-volatile writes. Among other usage contexts, lazySet may apply when nulling out, for the sake of garbage collection, a reference that is never accessed again.

weakCompareAndSetatomically reads and conditionally writes a variable but does not create any happens-before orderings, so provides no guarantees with respect to previous or subsequent reads and writes of any variables other than the target of the weakCompareAndSet. compareAndSet and all other read-and-update operations such as getAndIncrement have the memory effects of both reading and writing volatile variables.

原子的访问和更新的内存效应通常遵循 volatiles 的规则,如 The Java? 的 17.4 节所述。语言规范。

get具有读取易失性变量的记忆效应。

set具有写入(分配)易失性变量的记忆效应。

lazySet具有写入(分配)易失性变量的内存效果,但它允许对后续(但不是先前)内存操作进行重新排序,这些操作本身不会对普通非易失性写入施加重新排序约束。在其他使用上下文中,lazySet 可能会在清空时应用,为了垃圾收集,一个永远不会再次访问的引用。

weakCompareAndSet原子地读取和有条件地写入变量,但不创建任何发生在排序之前,因此不提供关于之前或后续读取和写入除 weakCompareAndSet 目标之外的任何变量的任何保证。compareAndSet 和所有其他读取和更新操作(例如 getAndIncrement)都具有读取和写入易失性变量的记忆效应。