在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/