在JLS的第17章 ,它引入一个概念:之前发生是一致的。
一组动作是之前发生一致的,如果所有的A,其中W(R)是由R看到的写入操作读取R,它是不是这样的,要么HB(R,W(R)),或出现存在一个写w的A使得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是不挥发领域。 所以我的第四quesiton是:我的理解是否正确?
在第二执行顺序:
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
发生在不同的线程或不)是之前发生关系之间定义X
和Y
。
如果这样的关系不存在,则JVM可重新排序的程序的操作。
现在,如果一个变量是共享,并通过多个线程访问,并通过(至少)一个线程写入,如果读取和写入不下令在发生关系之前 ,那么你有一个数据的比赛。
在一个正确的程序没有数据竞争。
实施例是2个线程A
和B
上锁定同步X
。
Thread A
获取锁(现在Thread B
被阻塞)并执行写入操作,然后释放锁X
。 现在Thread B
获得锁X
以来的一切行动Thread A
被释放锁之前完成X
,他们的行动之前下令 Thread B
,其获取锁X
线后 A
(也可见Thread B
)。
请注意,这发生在对同一个锁同步动作。 有不同的锁同步线程之间的关系之前, 没有发生
在物质是正确的。 拿出这样做的主要事情是:除非你使用某种形式的同步,也不能保证,在你的程序顺序写入之后,是读看到的写的影响,因为报表可能已reodered。
它存在不存在这种情况(见读取较晚出现的写操作)在现实世界中? 如果是这样,你可以给我一个真实的例子吗?
从挂钟的角度来看,很明显,一个读不能看到,还没有发生的事情写的效果。
从程序顺序的角度来看,因为语句可以,如果没有一个正确的同步进行重新排序,因为它已经被执行(关系之前发生),在你的程序写入之前出现的读取,可以看到执行过程中写的效果后由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是不挥发系统可以重新排列变量的评价,并有可能成为直觉。