yan*_*kee 53 java multithreading java-memory-model
在以下代码中:
class A {
private int number;
public void a() {
number = 5;
}
public void b() {
while(number == 0) {
// ...
}
}
}
Run Code Online (Sandbox Code Playgroud)
如果调用方法b然后启动一个触发方法a的新线程,则方法b不能保证看到更改,number因此b可能永远不会终止.
当然,我们可以number volatile解决这个问题.但是出于学术原因,我们假设这volatile不是一个选择:
该JSR-133的常见问题告诉我们:
在我们退出synchronized块之后,我们释放了监视器,它具有将缓存刷新到主内存的效果,因此该线程所做的写操作对其他线程是可见的.在我们进入同步块之前,我们获取监视器,它具有使本地处理器高速缓存无效的效果,以便从主存储器重新加载变量.
这听起来像我只需要两个a并b进入和退出任何synchronized-Block,无论他们使用什么显示器.更确切地说,它听起来像这样......:
class A {
private int number;
public void a() {
number = 5;
synchronized(new Object()) {}
}
public void b() {
while(number == 0) {
// ...
synchronized(new Object()) {}
}
}
}
Run Code Online (Sandbox Code Playgroud)
...将消除问题并保证b将看到更改a,因此也将最终终止.
然而,常见问题解答也明确指出:
另一个含义是,一些人用来强制内存屏障的以下模式不起作用:
Run Code Online (Sandbox Code Playgroud)synchronized (new Object()) {}这实际上是一个无操作,并且您的编译器可以完全删除它,因为编译器知道没有其他线程将在同一监视器上同步.您必须为一个线程设置一个before-before关系,以查看另一个线程的结果.
现在这令人困惑.我认为synchronized-Statement将导致缓存刷新.它肯定无法将缓存刷新到主内存,因为主内存中的更改只能由同一监视器上同步的线程看到,特别是因为对于volatile而言基本上做同样的事情我们甚至不需要监视器,还是我错了?那么为什么这是一个无操作,并且不会导致b保证终止?
ysh*_*vit 49
常见问题解答不是此事的权威; JLS是.第17.4.4节规定了同步关系,它们提供了先发生关系(17.4.5).相关要点是:
- 显示器上的解锁动作米 同步-与上所有后续锁定动作米(其中,"随后的"是根据该同步命令定义).
由于m是new Object()对它的引用,并且它从未存储或发布到任何其他线程,因此我们可以确定在释放此块中的锁之后,没有其他线程将获取对m的锁定.此外,由于m是一个新对象,我们可以确定没有先前已解锁的动作.因此,我们可以确定没有任何操作正式与此操作同步.
从技术上讲,您甚至不需要执行完全缓存刷新以达到JLS规范; 它不仅仅是JLS的要求.一个典型的实现就是这样,因为它是硬件允许你做的最简单的事情,但它可以说是"超越".在转义分析告诉优化编译器我们需要更少的情况下,编译器可以执行更少的操作.在您的示例中,转义分析可以告诉编译器该操作没有效果(由于上面的推理)并且可以完全优化.
Sol*_*low 20
以下模式,有些人用来强制内存屏障,不起作用:
它不能保证是无操作,但规范允许它成为无操作.当两个线程在同一个对象上同步时,规范只需要同步以在两个线程之间建立一个先发生的关系,但实际上更容易实现一个JVM,其中对象的标识无关紧要.
我认为synchronized-Statement将导致缓存刷新
Java语言规范中没有"缓存".这个概念只存在于某些(好的,好的,几乎所有)硬件平台和JVM实现的细节中.