问题

lazySet和911262754的方法有什么区别?AtomicInteger? Thedocumentation对于6976763019没什么好说的:

最终设置为给定值。

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


#1 热门回答(100 赞)

直接引用自Bug 6275329

可能是Mustang的最后一个JSR166后续跟踪,我们在Atomic类(AtomicInteger,AtomicReference等)中添加了一个"lazySet"方法。这是一种利基方法,在使用非阻塞数据结构微调代码时有时很有用。语义是保证写入不会与任何先前的写入一起重新排序,但可以与后续操作重新排序(或者等效地,可能对其他线程不可见),直到发生一些其他易失性写入或同步动作)。主要用例是仅为了避免长期垃圾保留而使非阻塞数据结构中的节点字段无效;如果其他线程暂时看到非空值,则它适用于无害,但你希望确保结构最终是GCable。在这种情况下,你可以通过避免null volatile-write的成本来获得更好的性能。对于非基于参考的原子,这些行还有一些其他用例,因此所有AtomicX类都支持该方法。对于那些喜欢在常见多处理器上的机器级障碍方面考虑这些操作的人来说,lazySet提供了一个先前的商店存储障碍(在当前平台上是无操作或非常便宜),但没有存储负载障碍(这通常是volatile-write的昂贵部分)。


#2 热门回答(13 赞)

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)上,这样的屏障比volatile或AtomicLong getAndAdd便宜得多,

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

我追踪Hotspot源代码,找到lazySet到cpp代码的确切映射: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。对于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


#3 热门回答(9 赞)

关于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解决竞争写入,这正是在incAndGet的Atomic *封面。


原文链接