AtomicXXX.lazySet(...) 在边缘之前发生

2022-09-02 13:44:06

在大多数JMM推理中使用的发生在边缘之前,AtomicXXX.lazySet(value)方法是什么意思?javadocs是纯粹的,Sun bug 6275329指出:

语义是保证写入不会与任何先前的写入重新排序,但可以通过后续操作重新排序(或者等效地,可能对其他线程不可见),直到发生其他一些易失性的写入或同步操作)。

但这不是关于HB边缘的推理,所以它让我感到困惑。这是否意味着懒惰Set()语义不能用HB边缘来表示?

更新:我会尝试具体化我的问题。我可以在以下场景中使用普通的易失性字段:

//thread 1: producer
...fill some data structure
myVolatileFlag = 1;

//thread 2: consumer
while(myVolatileFlag!=1){
   //spin-wait
}
...use data structure...

在这种情况下,在消费者中使用“数据结构”是正确的,因为易失性标志写读使HB成为边缘,从而保证了生产者对“数据结构”的所有写入都将完成,并且消费者可见。但是,如果在这种情况下,我将使用AtomicInteger.lazySet/get而不是易失性的写入/读取呢?

//thread 1: producer
...fill some data structure
myAtomicFlag.lazySet(1);

//thread 2: consumer
while(myAtomicFlag.get()!=1){
   //spin-wait
}
...use data structure...

它仍然是正确的吗?我还能真正关注“数据结构”值在消费者线程中的可见性吗?

这不是“从空中”的问题 - 我在LMAX Disruptor代码中看到了这种方法,在这种情况下,我不明白如何证明它是正确的......


答案 1

这些操作不会在边之前创建发生,因此不能保证立即可见。这是一个低级优化,只有几个用例,这些用例大多在并发数据结构中。lazySet

将链接列表指针清空的垃圾回收示例没有用户可见的副作用。首选清空,这样,如果列表中的节点属于不同的代,则不会强制执行更昂贵的收集来丢弃链接链。使用 lazySet 可以保持卫生语义,而不会产生易失性的写入开销。

另一个示例是使用由锁保护的易失性字段,例如 在 中。这些字段是易失性的,允许无锁读取,但写入必须在锁下执行,以确保严格的一致性。由于锁保证了发布时的边缘发生之前,因此优化是在写入字段时使用 lazySet,并在解锁时刷新所有更新。这有助于通过避免不必要的失速和公交车流量来保持关键部分的短距离。ConcurrentHashMap

如果您编写并发数据结构,那么这是一个需要注意的好技巧。它是一种低级优化,因此在性能调整时唯一值得考虑。lazySet


答案 2

基于不安全的Javadoc(putOrderedInt用于AtomicInteger.lazySet)

/** 
 * Version of {@link #putObjectVolatile(Object, long, Object)}
 * that does not guarantee immediate visibility of the store to
 * other threads. This method is generally only useful if the
 * underlying field is a Java volatile (or if an array cell, one
 * that is otherwise only accessed using volatile accesses).
 */
public native void  putOrderedObject(Object o, long offset, Object x);

/** Ordered/Lazy version of {@link #putIntVolatile(Object, long, int)}  */
public native void  putOrderedInt(Object o, long offset, int x);

AtomicXXX 类中的后备字段是易失性的。lazySet似乎会像不易失性一样写入这些字段,这将删除您期望的发生之前的边缘。如您的链接中所述,这对于使值符合 GC 条件而不必产生易失性写入非常有用。

编辑:

这是为了回答您的更新。

如果您查看从链接中提供的报价,那么您将失去对易失性写入的任何内存保证。

懒惰集不会在写入位置的上方排序,但是如果没有任何其他实际同步,您将无法保证使用者将看到之前写入的任何更改。延迟myAtomicFlag的写入是完全合法的,并且在它之前的任何写入都是完全合法的,直到发生某种其他形式的同步。


推荐