AtomicInteger 惰性集与集合

AtomicIntegerlazySetset方法有什么不同?关于 lazySet文件没有太多要说的:

最终设置为给定的值。

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

33340 次浏览

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

作为野马 JSR166的最后一次跟进, 我们向 Atomicclass 添加了一个“ lazySet”方法 (AtomicInteger、 AtomicReference 等) 对代码进行微调时有时非常有用的方法 非阻塞数据结构 保证写操作不会被重新排序 上一次写入,但可以在后续操作中重新排序 (或等效地,可能对其他线程不可见) ,直到 发生一些其他易失的写操作或同步操作)。

中的节点字段为空 非阻塞数据结构仅仅是为了避免 长期的垃圾保留; 它适用于无害的情况 如果其他线程在一段时间内看到非空值,但您会 希望确保结构最终是 GCable 例,你可以得到更好的表现,避免 空易失性写的代价。有一些 其他沿着这些线路的非基于引用的用例 原子,因此该方法在所有 AtomicX 类。

对于那些喜欢将这些操作视为 通用多处理器上的机器级障碍 提供前面的存储-存储屏障(即 在目前的平台上是不可行的或者非常便宜的) ,但是不行 储存-负载屏障(这通常是昂贵的部分 易失性写入)。

回复: 试图简化它-

您可以将此视为一种处理可变字段的方法,就好像它对于特定的存储操作(例如: ref = null;)不是可变的一样。

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

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

更广泛的关于 lazySet 的起源和实用性的讨论以及潜在的 putOrded 可以在这里找到: http://psy-lob-saw.blogspot.co.uk/2012/12/atomiclazyset-is-performance-win-for.html

总结一下: lazySet 是一种弱可变写,因为它充当存储区而不是存储负载隔离墙。这可以归结为 lazySet 被 JIT 编译成一个 MOV 指令,编译器不能重新排序,而不是用于易失性集的昂贵指令。

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

LazySet 为单个写入器提供了一个一致的可变写入机制,也就是说,单个写入器使用 lazySet 增加一个计数器是完全合法的,增加同一个计数器的多个线程必须使用 CAS 解决相互竞争的写入,这正是在 incAndGet 的 Atomic * 的掩护下发生的情况。

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

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

用于 x86 CPU 架构的 Oracle 热点 jdk->

LazySet = = unsafe.putOrderedLong = = xchg rw (在 Nehelem Intel CPU 上作为软屏障的指令,需要花费20个循环)

在 x86(x86 _ 64)上,这样的屏障在性能方面要比 volic 或 AtomicLong getAndAdd 便宜得多,

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

我跟踪 Hotspot 的源代码,找到 lazySet 到 cpp 代码的确切映射: Http://hg.openjdk.java.net/jdk7/jdk7/hotspot/file/9b0ca45cd756/src/share/vm/prims/unsafe.cpp 不安全 _ setOrderedLong-> SET _ FIELD _ VONATILE 定义-> OrderAccess: release _ store _ ence。 对于 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 吗

来自 并发原子包摘要

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

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

对原子的访问和更新的内存效应 请遵循 The JavaTM 第17.4节中所述的针对 volals 的规则 语言规格。

Get 具有阅读 Volatile变量的记忆效果。

Set 具有写(分配) Volatile变量的记忆效果。

LazySet 具有写(分配) Volatile变量的记忆效果,除了它允许使用后续(但不是先前)内存操作进行重新排序,而这些操作本身并不强制重新排序 约束与普通非易失性写。其他用法 为了处理垃圾,当执行 null 操作时,可能会应用 lazySet 集合,这是一个永远不会再访问的引用。

RowCompareAndSet 原子地读取和有条件地写入变量,但不创建任何幼稚的——在排序之前,因此提供 对于前面或后面的读和写没有保证 的目标以外的任何变量。

CompareAndSet 和所有其他读和更新操作(如 getAndIncrement)都具有读和写的内存效果 易失性变量。