在下面的代码中:
class A {
private int number;
public void a() {
number = 5;
}
public void b() {
while(number == 0) {
// ...
}
}
}
如果调用方法 b,然后启动一个新线程来触发方法 a,则不能保证方法 b 能够看到number
因此b
可能永远不会终止。
当然我们可以做number
volatile
来解决这个问题。然而出于学术原因让我们假设volatile
不是一个选项:
The JSR-133 常见问题解答 http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#synchronization告诉我们:
退出同步块后,我们释放监视器,这具有将缓存刷新到主内存的作用,以便其他线程可以看到该线程所做的写入。在进入同步块之前,我们需要获取监视器,该监视器具有使本地处理器缓存失效的效果这样变量将从主内存中重新加载。
听起来我只需要两者a
and b
进入和退出任何synchronized
- 完全阻止,无论他们使用什么显示器。更准确地说,它听起来像这样......:
class A {
private int number;
public void a() {
number = 5;
synchronized(new Object()) {}
}
public void b() {
while(number == 0) {
// ...
synchronized(new Object()) {}
}
}
}
...将消除问题并保证b
将看到更改为a
因此也最终会终止。
然而,常见问题解答也明确指出:
另一个含义是以下模式,有些人认为
用于强制内存屏障,不起作用:
synchronized (new Object()) {}
这实际上是一个空操作,你的编译器可以完全删除它,
因为编译器知道没有其他线程会同步
同一个显示器。您必须为以下内容建立一个发生在关系之前的关系:
一个线程查看另一个线程的结果。
现在这很令人困惑。我认为同步语句会导致缓存刷新。它肯定不能将缓存刷新到主内存,因为主内存中的更改只能由在同一监视器上同步的线程看到,特别是因为对于基本上执行相同操作的 volatile,我们甚至不需要监视器,还是我弄错了?那么为什么这是一个空操作并且不会导致b
通过保证终止?