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



但这不是关于HB边缘的原因,因此使我感到困惑。这是否意味着不能根据HB边缘表达什么lazySet()语义?

更新:我将尝试具体化我的问题。我可以在以下情况下使用普通的volatile字段:

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

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

在这种情况下,在使用者中使用“数据结构”是正确的,因为 volatile 标志的读写使HB边缘,从而保证了生产者对“数据结构”的所有写操作都将完成,并且对使用者可见。但是,如果在这种情况下我将使用AtomicInteger.lazySet/get而不是 volatile 读写,该怎么办?
//thread 1: producer
...fill some data structure
myAtomicFlag.lazySet(1);

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

仍然正确吗?我是否仍然可以真正了解“数据结构”值在使用者线程中的可见性?

这不是“来自空中”的问题-正是在这种情况下,我已经在LMAX Disruptor代码中看到了这种方法,而且我不知道如何证明它是正确的...

最佳答案

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

使链接列表指针无效的垃圾回收示例没有用户可见的副作用。最好使用空值,以便如果列表中的节点处于不同的世代,则不会强制执行更昂贵的收集以丢弃链接链。使用lazySet可以保持卫生的语义,而不会引起可变的写开销。

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

如果编写并发数据结构,那么lazySet是一个不错的技巧。它是低级优化,因此仅在性能调整时值得考虑。

10-06 15:34