如何理解发生 – 在一致之前

在JLS的第17章中 ,它引入了一个概念:在一致之前发生。

如果对于A中的所有读取r,其中W(r)是r看到的写入动作,那么一组动作A发生 – 在一致之前,不是hb(r,W(r))或那里的情况在A中存在写w,使得wv = rv和hb(W(r),w)和hb(w,r)“

在我的理解中,它等于以下几个词:……,既不是……也不是……

所以我的前两个问题是:

  • 我的理解对吗?
  • “wv = rv”是什么意思?

它还给出了一个例子: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 

订单本身已经告诉我们两个线程交替执行,所以我的第三个问题是:左数是什么意思?

根据我的理解,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; 

根据先发生一致性的定义,不难理解这种执行顺序是在一致之前发生的(如果我的第一个理解是正确的)。 所以我的第五和第六个问题是:在现实世界中是否存在这种情况(读取后面会发生的写入)? 如果确实如此,你能给我一个真实的例子吗?

每个线程可以在具有自己的缓存的不同核心上。 这意味着一个线程可以写入存储在寄存器或其本地缓存中的值,并且该值在一段时间内对另一个线程不可见。 (毫秒并不少见)

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

在这两种情况下,使用volatile确保读取和写入以一致的顺序发生,并且两个线程都看到相同的值。 这有时被描述为始终从主存储器读取,但并非必须如此,因为高速缓存可以直接相互通信。 (因此,性能影响远小于您的预期)

Java内存模型定义了程序的所有操作的部分排序 ,称为先发生过
为了保证线程Y能够看到动作X的副作用(如果X发生在不同的线程中,则无关),在XY之间定义了之前发生的关系。
如果不存在这种关系,则JVM可以重新排序程序的操作。
现在,如果一个变量被许multithreading共享和访问,并且由(至少)一个线程写入,如果读取和写入不是由关系之前事件排序,那么你就有一个数据竞争。
在正确的程序中没有数据竞赛。
示例是在锁X上同步的2个线程AB
Thread A获取锁定(现在Thread B被阻止)并执行写入操作,然后释放锁定X 现在, Thread B获取了锁X并且因为Thread A所有动作都是在释放X 之前完成的,所以它们 Thread B的动作之前排序Thread B 线程A 之后获得了锁X (并且对Thread B也是可见的)。
请注意,这发生在同一锁上同步操作在不同锁上同步的线程之间的关系之前没有发生

实质上是正确的。 要解决的主要问题是:除非您使用某种forms的同步,否则无法保证在您的程序顺序中写入之后的读取会看到该写入的效果,因为语句可能已被重新编码。

在现实世界中是否存在这种情况(读取后面会发生的写入)? 如果确实如此,你能给我一个真实的例子吗?

从挂钟的角度来看,显然,读取无法看到尚未发生的写入效果。

从程序顺序的角度来看,因为如果没有正确的同步(在关系之前发生),语句可以重新排序,在程序中写入之前的读取可以在执行期间看到该写入的效果,因为它已被执行在JVM写入之后。

这意味着如果没有适当的同步机制,您可以直观地看到影响计数器。

 volatile int A = 0; volatile int B = 0; 1: B = 1; 2: r2 = A; // r2 guarantees the value 1 3: A = 2; 4: r1 = B; // r1 guarantees the value 2 

这是因为volatile变量保证在关系之前发生。 如果A和B不是易失性系统可以重新排序变量的评估,它可能变得反直觉。