为什么`synchronized(new Object()){}`没有操作?

在下面的代码中:

class A { private int number; public void a() { number = 5; } public void b() { while(number == 0) { // ... } } } 

如果方法b被调用,然后一个新的线程开始引发方法a,那么方法b不能保证看到数字的变化,因此b可能永远不会终止。

当然,我们可以通过number来解决这个问题。 但是出于学术的原因,我们假设volatile不是一个select:

JSR-133常见问题告诉我们:

在我们退出一个同步块之后,我们释放监视器, 将caching刷新到主内存 ,这样这个线程所做的写操作对其他线程是可见的。 在我们可以input一个同步块之前,我们需要获取监视器,这会使本地处理器caching失效,从而使variables从主内存中重新加载。

这听起来就像我只需要ab可以进入和退出任何synchronized -Block,不pipe他们使用什么监视器。 更确切地说,这听起来像这样…:

 class A { private int number; public void a() { number = 5; synchronized(new Object()) {} } public void b() { while(number == 0) { // ... synchronized(new Object()) {} } } } 

…会消除这个问题,并保证b会看到b的变化,从而最终也会终止。

但是常见问题解答还明确指出:

另一个含义是有些人用来强制记忆障碍的下列模式不起作用:

 synchronized (new Object()) {} 

这实际上是一个空操作,编译器可以完全删除它,因为编译器知道没有其他线程将在同一监视器上同步。 你必须为一个线程build立一个前后关系来查看另一个线程的结果。

现在令人困惑。 我认为synchronized语句会导致caching刷新。 它肯定无法刷新caching到主内存的方式,主内存中的变化只能被在同一监视器上同步的线程看到,特别是因为对于基本上同样的事情我们甚至不需要显示器,还是我误解了? 那么,为什么这是一个无操作,并不会导致b保证终止?

常见问题不是关于此事的权威。 JLS是。 17.4.4节规定了在关系之前(17.4.5)与同步关系发生同步。 相关的要点是:

  • 监视器m上的解锁操作与m 上的所有后续locking操作(其中“后续”根据同步顺序定义)同步。

由于这里的m是对new Object()的引用,并且它从不存储或发布到任何其他线程,所以我们可以确定,在此块的锁被释放之后,没有其他线程会获得对m的锁。 而且,由于m是一个新的对象,所以我们可以肯定,以前没有任何解锁的动作。 因此,我们可以肯定,没有任何行动正式与这一行动同步。

从技术上讲,你甚至不需要做一个完整的高速caching刷新,以达到JLS规范; 这不仅仅是JLS的要求。 一个典型的实现是这样做的,因为这是硬件最容易让你做的事情,但是可以这么说。 在逃逸分析告诉优化编译器我们需要更less的情况下,编译器可以执行的更less。 在你的例子中,逃逸分析可以告诉编译器,这个动作没有任何效果(由于上面的推理),可以完全优化。

有些人用来强制记忆障碍的下列模式不起作用:

它不能保证是一个没有操作,但规范允许它是一个没有操作。 规范只需要同步就可以在两个线程在同一个对象上同步时在两个线程之间build立一个“先发生”的关系,但是实际上在对象的身份不重要的情况下实现一个JVM会更容易。

我认为synchronized语句会导致caching刷新

Java语言规范中没有“caching”。 这个概念只存在于一些(很好,几乎所有的)硬件平台和JVM实现的细节中。