java - AtomicXXX.lazySet(...) 就 happens before edges 而言

标签 java concurrency atomic

在大多数 JMM 推理中使用的 AtomicXXX.lazySet(value) 方法是什么意思? javadocs 是纯粹的,Sun bug 6275329状态:

The semantics are that the write is guaranteed not to be re-ordered with any previous write, but may be reordered with subsequent operations (or equivalently, might not be visible to other threads) until some other volatile write or synchronizing action occurs).

但这不是关于 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 write/read 呢?

//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 操作不会创建 happens-before 边,因此不能保证立即可见。这是一种低级优化,只有少数用例,主要在并发数据结构中。

清除链表指针的垃圾收集示例没有用户可见的副作用。首选 nulling,这样如果列表中的节点处于不同的世代,它不会强制执行更昂贵的收集来丢弃链接链。 lazySet 的使用维护了 hygenic 语义,而不会产生不稳定的写入开销。

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

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

关于java - AtomicXXX.lazySet(...) 就 happens before edges 而言,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7557156/

相关文章:

c++ - 为什么 std::atomic<T>::compare_exchange_* 不应遭受 ABA 问题?

multithreading - 互斥体、原子和栅栏 : what offers the best tradeoff and portability ? C++11

java - forRevisionsOfEntity 很慢

java - 获取Java webapp中的所有用户和角色

java - 在 ThreadLocal 中包装 JDBC 连接单例

c++ - 并发读取非原子变量

java - 使用 Java Toast 调用非静态方法

java - 使用自定义错误消息将用户输入转换为整数

go - 同时多次下载同一文件

java - 选择锁定为 "FOR UPDATE"的 MySQL 记录时会抛出什么异常?