JLS中关于JMM之前发生的关系(第17.4.5节)是
应该注意到之前发生的关系 两次行动之间并不一定意味着必须采取行动 在实施中以该顺序放置。
我对此声明的例子很感兴趣。
我理解这是正确的,作为一个例子可能如下:
Thread 1
x = 1
lock M
y = 2
unlock M ----------------------->Thread 2:
lock M
z = 3
w = 4
unlock M
显然,在这个执行跟踪中,(x = 1)和(w = 4)之间存在一个先发生的关系:
(x = 1) happens-before (w = 4)
同样在这种情况下(x = 1)在执行跟踪中以(w = 4)排序。
Thread 1
不使用变量w
。因此,我们可以在(x = 1)之前放置它而不违反Thread 1
和Thread 2
的逻辑。
这是否意味着如果我们重新排序(x = 1)和(w = 4)然后发生 - 在这些语句之间的关系保留之前?
如果您有其他一些例子,请提供。
答案 0 :(得分:1)
是的,你是对的,发生在 - 当独立数据发生变化时,关系成立之前。至于其他示例,请不要忘记发生之前 - 也适用于同一线程中的事件。规则很简单:
如果 x 和 y 是同一个线程的操作, x 按照程序顺序出现在 y 之前,然后 hb(x,y)。
因此,Java方法中的每个语句都会在每个后续语句之前发生,但当然JIT编译器和CPU可以自由地重新排序独立语句(并且它们实际上经常这样做以优化性能)。有时你可以从另一个没有发生过的线程中观察这个重新排序 - 在与当前线程的关系之前。
答案 1 :(得分:1)
线程1不使用变量w
您不能认为这是一个原因,因为实际上,Java内存模型没有考虑在线程隔离的上下文中重新排序指令是否安全,关于其他人的感知涉及线程。
如果没有锁定或内存障碍,JMM只能保证在同一个线程中的语句之前发生。
在您的情况下,您在同一个对象(M)上有一个锁定机制,因此"发生在"之前"发生。
Java中的synchronized
(锁定)或其他原子变量处理内存障碍和原子性。
有关信息,构造函数中的volatile
变量和final
变量分配仅处理内存障碍。
以此example为例,根本不处理任何内存障碍:
Class Reordering {
int x = 0, y = 0;
public void writer() {
x = 1;
y = 2;
}
public void reader() {
int r1 = y;
int r2 = x;
}
}
让我们说这个代码同时在两个线程中执行,并且 y的读数看到值2。 因为这写在写入x之后,程序员可能会认为x的读取必须看到 值1.但是,写入可能已重新排序。如果这 发生,然后写入y可能发生,两者的读取 变量可以跟随,然后写入x可能会发生。该 结果是r1的值为2,但r2的值为0.