AtomicInteger lazySet vs. set

2022-08-31 08:36:00

和 方法有什么区别?文档没有太多要说的:lazySetsetAtomicIntegerlazySet

最终设置为给定值。

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


答案 1

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

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

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

对于那些喜欢从常见多处理器上的机器级障碍的角度来考虑这些操作的人来说,lazySet提供了一个预先的存储 - 存储屏障(在当前平台上要么是no-op,要么是非常便宜的),但没有存储负载屏障(这通常是易失性写入的昂贵部分)。


答案 2

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

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

oracle hotspot jdk for x86 cpu architecture->

lazySet == unsafe.putOrderedLong == xchg rw( asm 指令,用作 nehelem intel cpu 上成本为 20 个周期的软屏障)

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

在一个生产者,一个消费者队列场景中,xchg软屏障可以强制在lazySet(sequence+1)之前发生代码行,以便生产者线程在任何将消费(处理)新数据的消费者线程代码之前发生,当然消费者线程将需要使用compareAndSet(序列, 序列 + 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 代码的程序集。

还有另一个相关的问题:使用xchg时,我们是否需要mfence。


推荐