Kum*_*alh 10 java concurrency multithreading java-memory-model
我有一些关于程序顺序以及它如何影响JMM中的重新排序.
在Java内存模型中,程序顺序(po)被定义为程序中每个线程中的操作的总顺序.根据JLS,这会导致发生前(hb)边缘:
如果
x
并且y
是同一个线程的动作并且在程序顺序x
之前y
,那么hb(x,y)(即x
发生在之前y
).
所以对于一个简单的程序P:
initially, x = y = 0
T1 | T2
-----------|-----------
1. r1 = x | 3. r2 = y
2. y = 1 | 4. x = r2
Run Code Online (Sandbox Code Playgroud)
我认为po(1,2)和po(3,4).因此,hb(1,2)和hb(3,4).
现在假设我想重新排序其中一些语句,给我P':
initially, x = y = 0
T1 | T2
-----------|-----------
2. y = 1 | 3. r2 = y
1. r1 = x | 4. x = r2
Run Code Online (Sandbox Code Playgroud)
根据这篇论文,我们可以重新排序任何两个相邻的语句(例如1和2),前提是重新排序不会消除任何有效执行中的任何传递发生 - 前沿.然而,由于hb是由po定义的(部分),并且po是线程动作的总顺序,在我看来,在不违反hb的情况下重新排序任何两个语句是不可能的,因此P'不是合法的转换.
我的问题是:
你错过了JLS的这一部分:
应该注意的是,两个动作之间存在的先发生关系并不一定意味着它们必须在实现中以该顺序发生.如果重新排序产生的结果与合法执行一致,则不是非法的.
在您的情况下,由于1和2不相关,因此可以翻转它们.现在如果2已经存在y = r1
,那么1必须在2之前发生以获得正确的结果.
多处理器执行会出现真正的问题.没有任何先发生的边界,T2可能会在1之前观察到2,无论执行顺序如何.
这是因为CPU缓存.假设T1以任何顺序执行1和2.由于之前没有发生边界,因此这些操作仍然在CPU缓存中,并且根据其他需要,包含结果2的缓存部分可以在包含结果1的缓存部分之前刷新.
如果T2在这两个缓存刷新事件之间执行,那么它将发生2已经发生且1没有,即2发生在1之前,只要T2知道.
如果不允许,则T1必须在1和2之间建立一个先发生的边界.
在Java中,有各种方法可以做到这一点.旧样式是将1和2放入单独的synchronized
块中,因为块的开始和结束synchronized
是发生在前的边界,即块之前的任何操作发生在块内的操作之前,块内的任何操作都发生在块之前阻止后的行动.