AtomicInteger lazySet 与 set

Posted

技术标签:

【中文标题】AtomicInteger lazySet 与 set【英文标题】:AtomicInteger lazySet vs. set 【发布时间】:2010-11-30 21:07:24 【问题描述】:

AtomicIntegerlazySetset方法有什么区别? documentation 对lazySet 没什么好说的:

最终设置为给定值。

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

【问题讨论】:

【参考方案1】:

直接引用自"JDK-6275329: Add lazySet methods to atomic classes":

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

主要用例是清空节点的字段 非阻塞数据结构仅仅是为了避免 垃圾长期滞留;它适用于无害的情况 如果其他线程有一段时间看到非空值,但你会 喜欢确保结构最终是 GCable。在这样的 情况下,您可以通过避免获得更好的性能 null volatile-write 的成本。有几个 非基于参考的其他用例 原子也是如此,因此该方法在所有 AtomicX 类。

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

【讨论】:

有人可以为我们其他人简化一下吗? :( Lazy 是非易失性版本(例如,不保证状态更改对范围内具有Atomic* 的所有线程可见)。 我不明白为什么 javadoc 这么差。 我相信他们最终会改变它。轰隆隆。 对于那些想了解更多关于存储/加载屏障以及为什么存储-存储屏障比存储-加载屏障便宜的人。这是一篇关于它的易于理解的文章。 mechanical-sympathy.blogspot.com/2011/07/…【参考方案2】:

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

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

用于 x86 cpu 架构的 oracle 热点 jdk->

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

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

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

还有一个相关的问题: Do we need mfence when using xchg

【讨论】:

很难理解你在这里得到了什么。你能澄清一下你的观点吗? "lazySet == unsafe.putOrderedLong == xchg rw( asm 指令作为软屏障,在 nehelem intel cpu 上花费 20 个周期) 在 x86 (x86_64) 上这样的屏障在性能方面要便宜得多比 volatile 或 AtomicLong getAndAdd" -> 据我所知,这不是真的。 lazySet/putOrdered 是对地址的 MOV,这就是 JMM 食谱将其描述为 x86 上的无操作的原因。【参考方案3】:

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

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

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

lazySet 为单个 writer 提供一致的 volatile 写入机制,即单个 writer 使用lazySet 递增计数器是完全合法的,递增相同计数器的多个线程将必须使用 CAS 解决竞争写入,这正是incAndGet 的 Atomic* 背后发生了什么。

【讨论】:

没错,为什么我们不能说这是一个简单的StoreStore 屏障,而不是 StoreLoad【参考方案4】:

来自Concurrent-atomic package summary

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

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

一般原子访问和更新的记忆效应 遵循 volatile 的规则,如 The Java™ 第 17.4 节所述 语言规范。

get 具有读取 volatile 变量的记忆效应。

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

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

weakCompareAndSet 以原子方式读取和有条件地写入变量,但不会创建任何先发生顺序,因此提供 不保证之前或之后的读取和写入 除了weakCompareAndSet 的目标之外的任何变量。

compareAndSet 和所有其他的读取和更新操作,例如 getAndIncrement 具有读取和写入的记忆效应 可变变量。

【讨论】:

【参考方案5】:

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

【讨论】:

你不是说compareAndSet吗?【参考方案6】:

Re: 尝试让它变笨 -

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

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

【讨论】:

以上是关于AtomicInteger lazySet 与 set的主要内容,如果未能解决你的问题,请参考以下文章

AtomicInteger类的理解与使用

AtomicInteger类的理解与使用

AtomicInteger类的理解与使用

AtomicInteger类的理解与使用

AtomicInteger类的理解与使用

Java中多线程,synchronized,与 AtomicInteger的问题