问题描述
在大多数JMM推理中使用的AtomicXXX.lazySet(value)方法在边缘发生之前是什么意思? Javadocs完全是基于它的,并且Sun bug 指出:
What does mean AtomicXXX.lazySet(value) method in terms of happens-before edges, used in most of JMM reasoning? The javadocs is pure on it, and Sun bug 6275329 states:
但这不是HB边缘的原因,这让我感到困惑。这是否意味着不能从HB边缘表达什么lazySet()语义?
But this not a reasoning about HB edges, so it confuses me. Does it mean what lazySet() semantics can't be expressed in terms of HB edges?
更新:我将尝试具体化我的问题。我可以在以下情况下使用普通的volatile字段:
UPDATE: I'll try to concretize my question. I can use ordinary volatile field in following scenario:
//thread 1: producer
...fill some data structure
myVolatileFlag = 1;
//thread 2: consumer
while(myVolatileFlag!=1){
//spin-wait
}
...use data structure...
在这种情况下,消费者使用数据结构是正确的,因为易失标志写入-read使HB边缘,保证生产者对数据结构的所有写操作都将完成,并且对消费者可见。但是,如果在这种情况下我将使用AtomicInteger.lazySet / get而不是volatile写入/读取怎么办?
In this scenario use of "data structure" in consumer is correct, since volatile flag write-read make HB edge, giving guarantee what all writes to "data structure" by producer will be completed, and visible by consumer. But what if I'll use AtomicInteger.lazySet/get instead of volatile write/read in this scenario?
//thread 1: producer
...fill some data structure
myAtomicFlag.lazySet(1);
//thread 2: consumer
while(myAtomicFlag.get()!=1){
//spin-wait
}
...use data structure...
是否仍然正确?我还能在消费者线程中真正了解数据结构值的可见性吗?
will it be still correct? Can I still really on "data structure" values visibility in consumer thread?
这不是来自空中的问题-我已经在LMAX Disruptor代码中看到了这种方法在这种情况下,我不明白如何证明它是正确的...
It is not "from air" question -- I've seen such method in LMAX Disruptor code in exactly this scenario, and I don't understand how to prove it is correct...
推荐答案
lazySet
操作不会在边缘之前创建事件,因此不能保证立即可见。这是一个低级的优化,只有几个用例,大部分用在并发数据结构中。
The lazySet
operations do not create happens-before edges and are therefore not guaranteed to be immediately visible. This is a low-level optimization that has only a few use-cases, which are mostly in concurrent data structures.
使链接列表指针为空的垃圾回收示例有没有用户可见的副作用。最好使用空值,这样,如果列表中的节点处于不同的世代,则不会强制执行更昂贵的收集以丢弃链接链。使用lazySet可以保持卫生的语义,而不会产生易失性的写开销。
The garbage collection example of nulling out linked list pointers has no user-visible side effects. The nulling is preferred so that if nodes in the list are in different generations, it doesn't force a more expensive collection to be performed to discard the link chain. The use of lazySet maintains hygenic semantics without incurring volatile write overhead.
另一个示例是使用受锁保护的易失性字段,例如 ConcurrentHashMap
。这些字段是易失性的,以允许无锁读取,但是必须在锁下执行写入以确保严格的一致性。由于锁保证了释放之前发生的事情,因此一种优化方法是在写入字段时使用lazySet,并在解锁时刷新所有更新。这样可以避免不必要的停顿和总线流量,从而使关键部分保持较短。
Another example is the usage of volatile fields guarded by a lock, such as in ConcurrentHashMap
. The fields are volatile to allow lock-free reads, but writes must be performed under a lock to ensure strict consistency. As the lock guarantees the happens-before edge on release, an optimization is to use lazySet when writing to the fields and flushing all of the updates when unlocking. This helps keep the critical section short by avoiding unnecessary stalls and bus traffic.
如果编写并发数据结构,则 lazySet
是个好技巧。它是低级优化,因此仅在性能调整时值得考虑。
If you write a concurrent data structure then lazySet
is a good trick to be aware of. Its a low-level optimization so its only worth considering when performance tuning.
这篇关于AtomicXXX.lazySet(...)在边缘之前发生的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!