Che*_*eng 109 java concurrency atomic
lazySet
和set
方法有AtomicInteger
什么区别?该文件并没有太多的话要说lazySet
:
最终设置为给定值.
似乎存储的值不会立即设置为所需的值,而是计划在将来的某个时间设置.但是,这种方法的实际用途是什么?任何例子?
yaw*_*awn 109
直接引自"JDK-6275329:将lazySet方法添加到原子类":
可能是Mustang的最后一个JSR166后续跟踪,我们在Atomic类(AtomicInteger,AtomicReference等)中添加了一个"lazySet"方法.这是一种利基方法,在使用非阻塞数据结构微调代码时有时很有用.语义是保证写入不会与任何先前的写入重新排序,但可以与后续操作重新排序(或者等效地,可能对其他线程不可见),直到发生一些其他易失性写入或同步动作).
主要用例是仅为了避免长期垃圾保留而使非阻塞数据结构中的节点字段无效; 如果其他线程暂时看到非空值,则它适用于无害,但您希望确保结构最终是GCable.在这种情况下,您可以通过避免null volatile-write的成本来获得更好的性能.对于非基于参考的原子,这些行还有一些其他用例,因此所有AtomicX类都支持该方法.
对于那些喜欢在常见多处理器上的机器级障碍方面考虑这些操作的人来说,lazySet提供了一个先前的商店存储障碍(在当前平台上是无操作或非常便宜),但没有存储负载障碍(这通常是volatile-write的昂贵部分).
por*_*hop 14
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 . cpp Unsafe_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代码的程序集.
还有一个相关的问题: 使用xchg时我们是否需要mfence
Nit*_*art 11
关于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*封面.
lazySet具有写入(赋值)volatile变量的记忆效应,除了它允许对后续(但不是先前的)内存操作进行重新排序,这些内存操作本身不会对普通的非易失性写入施加重新排序约束.在其他用法上下文中,为了垃圾收集,可以在归零时应用lazySet,这是一个永远不会再次访问的引用.
如果你对lazySet很好奇,那么你也欠其他解释
访问和更新原子的记忆效应通常遵循挥发性规则,如Java™语言规范第17.4节所述.
get具有读取volatile变量的记忆效应.
set具有写入(赋值)volatile变量的记忆效应.
lazySet具有写入(赋值)volatile变量的记忆效应,除了它允许对后续(但不是先前的)内存操作进行重新排序,这些内存操作本身不会对普通的非易失性写入施加重新排序约束.在其他用法上下文中,为了垃圾收集,可以在归零时应用lazySet,这是一个永远不会再次访问的引用.
weakCompareAndSet以原子方式读取并有条件地写入变量,但不会创建任何先前发生的排序,因此不提供与weakCompareAndSet目标之外的任何变量的先前或后续读取和写入相关的保证.compareAndSet和所有其他读取和更新操作(如getAndIncrement)都具有读取和写入volatile变量的内存效果.
小智 5
这是我的理解,如果我错了请纠正我:您可以将其视为lazySet()
“半”易失性:就其他线程读取而言,它基本上是一个非易失性变量,即由lazySet设置的值可能对其他线程不可见线程。但当另一个写操作发生时(可能来自其他线程),它就会变得不稳定。我能想象到的lazySet的唯一影响是compareAndSet
。因此,如果您使用lazySet()
,get()
从其他线程可能仍会获取旧值,但compareAndSet()
将始终具有新值,因为它是写入操作。