java - 如何理解happens-before一致性

标签 java memory-model

chapter 17 of JLS ,引入了一个概念:happens-before consistent。

A set of actions A is happens-before consistent if for all reads r in A, where W(r) is the write action seen by r, it is not the case that either hb(r, W(r)) or that there exists a write w in A such that w.v = r.v and hb(W(r), w) and hb(w, r)"

在我的理解中,相当于下面的话: ...,情况既不是...也不是...

所以我的前两个问题是:

  • 我的理解对吗?
  • “w.v = r.v”是什么意思?

它还给出了一个例子:17.4.5-1

Thread 1 Thread 2

B = 1; A = 2; 

r2 = A; r1 = B; 

在第一个执行顺序:

1: B = 1;

3: A = 2;

2: r2 = A;  // sees initial write of 0

4: r1 = B;  // sees initial write of 0

顺序本身已经告诉我们两个线程交替执行,所以我的第三个问题是:left number是什么意思?

在我的理解中,r2和r1都可以看到初始写入0的原因是A和B都不是volatile字段。那么我的第四个问题是:我的理解是否正确?

在第二个执行顺序中:

1: r2 = A;  // sees write of A = 2

3: r1 = B;  // sees write of B = 1

2: B = 1;

4: A = 2;

根据happens-before consistency的定义,不难理解这个执行顺序是happens-before consistent(如果我的第一个理解是正确的话)。 所以我的第五个和第六个问题是:在现实世界中是否存在这种情况(读后写)?如果是的话,你能给我一个真实的例子吗?

最佳答案

每个线程都可以位于不同的核心上,具有自己的私有(private)寄存器,Java 可以使用这些寄存器来保存变量的值,除非您强制访问一致的共享内存。这意味着一个线程可以写入存储在寄存器中的值,并且该值在一段时间内对另一个线程不可见,例如循环或整个函数的持续时间。 (毫秒并不少见)

一个更极端的例子是,读取线程的代码是基于假设优化的,因为它永远不会改变值,所以它不需要从内存中读取它。在这种情况下,优化代码永远不会看到另一个线程执行的更改。

在这两种情况下,volatile 的使用确保读取和写入以一致的顺序发生,并且两个线程看到相同的值。有时这被描述为总是从主内存读取,但不一定是这种情况,因为缓存可以直接相互通信。 (因此性能影响比您预期的要小得多)。

在普通 CPU 上,缓存是“一致的”(不能保存陈旧/冲突的值)和透明的,不是手动管理的。使数据在线程之间可见仅意味着在 asm 中执行实际的加载或存储指令以访问内存(通过数据缓存),并可选择等待存储缓冲区耗尽以给出排序。其他后续操作。

关于java - 如何理解happens-before一致性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11970428/

相关文章:

java - 如何在 MySQL 中生成大量唯一的随机数

java - 如何在 Eclipse 编辑器中查看日志文件,就像在控制台窗口中一样?

java - Spring JPA - 序列缓存会产生意想不到的行为。使用分配大小 = 1 就可以了

C++ 如何仅使用 MOV 在 x86 上实现释放和获取?

c# - .NET 中的后续写入能否由运行时或处理器重新排序?

c++ - atomic<T>.load() 与 std::memory_order_release

java - java中将字符串转换为二维数组

java - 如何将私钥与证书链绑定(bind)?

c++ - C++0x 中的栅栏,一般只保证原子或内存

x86 - 为什么原子 RMW 指令的加载部分不能将较早的存储传递到 TSO(x86) 内存一致性模型中的不相关位置?