java - 根据 java 内存模型,synchronized 到底做了什么?

java - what does synchronized really do according the java memory model?

阅读了一些关于 java 内存模型和同步的内容后,出现了几个问题:

Even if Thread 1 synchronizes the writes, then although the effect of the writes will be flushed to main memory, Thread 2 will still not see them because the read came from level 1 cache. So synchronizing writes only prevents collisions on writes. (Java thread-safe write-only hashmap)

Second, when a synchronized method exits, it automatically establishes a happens-before relationship with any subsequent invocation of a synchronized method for the same object. This guarantees that changes to the state of the object are visible to all threads. (https://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html)

第三个网站(我找不到了,抱歉)说任何对象的每一次改变——它不关心引用来自哪里——当方法离开同步块时将被刷新到内存并建立 先发生 的情况。

我的问题是:

  1. 退出synchronized块真正刷回内存的是什么? (有的网站也说,只有获得锁的对象才会被flush回来。)

  2. 在这种情况下,happens-before-relaitonship 是什么意思?什么会在进入块时从内存中重新读取,什么不是?

  3. 锁是如何实现这个功能的(来自https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/Lock.html):

    All Lock implementations must enforce the same memory synchronization semantics as provided by the built-in monitor lock, as described in section 17.4 of The Java™ Language Specification:

    A successful lock operation has the same memory synchronization effects as a successful Lock action. A successful unlock operation has the same memory synchronization effects as a successful Unlock action. Unsuccessful locking and unlocking operations, and reentrant locking/unlocking operations, do not require any memory synchronization effects.

如果我关于所有内容都将被重新读取和刷新的假设是正确的,那么这是通过在锁定和解锁功能(大多数也是必要的)中使用同步块来实现的,对吧?而如果错了,这个功能怎么实现呢?

提前致谢!

happens-before-relationship 是您必须了解的基本内容,因为 the formal specification 根据这些进行操作。像“冲洗”这样的术语是技术细节,可以帮助您理解它们,或者在最坏的情况下误导您。

如果线程在 synchronized(object1) { … } 中执行操作 A,随后线程在 synchronized(object1) { … } 中执行操作 B,假设 object1 引用对于同一个对象,在 AB 之间存在 happens-before-relationship 并且这些操作对于访问共享可变数据是安全的(假设,没有人否则修改此数据)。

但这是一种定向关系,即B可以安全地访问A修改的数据。但是当看到两个synchronized(object1) { … }块时,在确定object1是同一个对象的情况下,还需要知道A是在B之前执行的还是B执行的在 A 之前执行,以了解 happens-before-relationship 的方向。对于普通的面向对象的代码,这通常很自然地起作用,因为每个动作都会对它找到的对象的任何先前状态进行操作。

说到刷新,离开 synchronized 块会刷新所有写入的数据,进入 synchronized 块会导致重新读取所有可变数据,但没有 [=25 的互斥保证=] 在同一个实例中,无法控制哪个发生在另一个之前。更糟糕的是,您无法使用共享数据来检测情况,因为在不阻塞其他线程的情况下,它仍然可以不一致地修改您正在操作的数据。

由于在不同对象上同步无法建立有效的happens-before关系,因此不需要JVM的优化器来维持全局刷新效果。最值得注意的是,如果 Escape Analysis 证明该对象从未被其他线程看到,那么今天的 JVM 将删除同步。

因此,您可以在一个对象上使用同步来保护对存储在其他地方(即不在该对象中)的数据的访问,但它仍然需要在同一对象实例上进行一致的同步以访问相同的共享数据,这使情况变得复杂程序逻辑,与简单地同步包含受保护数据的同一对象相比。


volatile 变量,就像 Locks 内部使用的一样,也有全局刷新效果,如果线程正在读写相同的 volatile 变量,并使用该值来形成正确的程序逻辑。这比 synchronized 块更棘手,因为没有代码执行的互斥,或者,你可以将其视为具有仅限于单个读、写或 cas 操作的互斥。

本身没有同花顺,只是这样想更容易(也更容易画);这就是为什么网上有很多资源提到 刷新到主内存(假设是 RAM),但实际上它并不经常发生。真正发生的是加载 and/or 存储缓冲区到 L1 缓存(如果是 IBM,则为 L2)并由缓存一致性协议从那里同步数据;或者换句话说,缓存足够智能,可以相互通信(通过总线),而不是一直从主内存中获取数据。

这是一个复杂的主题(免责声明:尽管我尝试对此进行大量阅读,有空时进行了很多测试,但我绝对不能完全理解它),它是关于潜在的compiler/cpu/etc 重新排序(从不尊重程序顺序),它是关于缓冲区的刷新,关于内存屏障,release/acquire 语义......我认为如果没有博士报告,你的问题是无法回答的;这就是为什么 JLS 中有更高层的原因 - "happens-before"。

至少理解了上面的一小部分,你就会明白你的问题(至少前两个)没有什么意义。

What is really flushed back to memory by exiting the synchronized block

可能什么都没有 - 相互缓存 "talk" 以同步数据;我只能想到另外两种情况:当你第一次读取一些数据时,当一个线程死掉时——所有写入的数据都会被刷新到主内存(但我不确定)。

What does happens-before-relaitonship mean in this case? And what will be re-read from memory on entering the block, what not?

真的,同上一句

How does a lock achieve this functionality

通常通过引入内存屏障;就像挥发物一样。