在阅读了一些关于java内存模型和同步的内容之后,出现了一些问题:
即使线程1同步写入,然后虽然写入的效果将刷新到主存储器,但线程2仍然看不到它们,因为读取来自1级高速缓存。因此,同步写入仅防止写入时发生冲突。 (Java thread-safe write-only hashmap)
其次,当synchronized方法退出时,它会自动与同一对象的同步方法的任何后续调用建立一个before-before关系。这可以保证对所有线程都可以看到对象状态的更改。 (https://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html)
第三个网站(我再也找不到了,抱歉)说,任何对象的每次更改 - 它都不关心引用的来源 - 当方法离开时,将被刷新到内存中同步块并建立发生前情况。
我的问题是:
通过退出synchronized块真正刷回内存的是什么? (正如一些网站还说只有被锁定的对象才会被冲回来。)
在这种情况下,在重新启动之前发生什么意味着什么?什么不会在进入块时从内存中重新读取什么?
锁定如何实现此功能(来自https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/Lock.html):
所有Lock实现必须强制执行内置监视器锁提供的相同内存同步语义,如Java™语言规范的第17.4节所述:
成功的锁定操作与成功的锁定操作具有相同的内存同步效果。 成功解锁操作具有与成功解锁操作相同的内存同步效果。 不成功的锁定和解锁操作以及重入锁定/解锁操作不需要任何内存同步效果。
如果我认为所有内容都将被重新读取和刷新是正确的,那么这可以通过在锁定和解锁功能中使用synchronized-block来实现(对于大多数情况下也是如此),对吗?如果它错了,怎么能实现这个功能呢?
提前谢谢!
答案 0 :(得分:2)
发生在关系之前是你必须要理解的基本事情,因为the formal specification就这些而言。像“刷新”这样的术语是可以帮助您理解它们的技术细节,或者在最坏的情况下误导您。
如果某个帖子在A
内执行了synchronized(object1) { … }
个操作,后跟在B
内执行操作synchronized(object1) { … }
的帖子,假设object1
指的是同一个对象,A
和B
之间存在在关系之前发生,这些操作在访问共享可变数据时是安全的(假设没有其他人修改此数据)
但这是一种有向关系,即B
可以安全地访问由A
修改的数据。但是,当看到两个synchronized(object1) { … }
块时,确保object1
是同一个对象,您仍然需要知道在A
或B
之前是否执行了B
在A
之前执行,以了解在关系之前发生的方向。对于普通的面向对象的代码,这通常是自然而然的,因为每个动作都将对它找到的对象的任何先前状态进行操作。
说到刷新,留下synchronized
块导致刷新所有书面数据并进入synchronized
块导致重新读取所有可变数据,但没有synchronized
的互斥保证在同一个实例中,无法控制哪一个发生在另一个之前。更糟糕的是,您不能使用共享数据来检测情况,因为如果不阻塞其他线程,它仍然会不一致地修改您正在操作的数据。
由于在不同对象上进行同步无法建立有效的发生在之前的关系,因此不需要JVM的优化器来维护全局刷新效果。最值得注意的是,如果Escape Analysis已经证明该对象永远不会被其他线程看到,那么今天的JVM将删除同步。
因此,您可以使用对象上的同步来保护对存储在其他位置的数据的访问,即不在该对象中,但是仍然需要在同一对象实例上进行一致的同步,以便对相同的共享数据进行所有访问,这会使程序复杂化逻辑,与简单地同步包含受保护数据的同一对象相比。
如果线程正在读取和写入相同的volatile
变量,那么 Lock
变量(如内部volatile
使用的变量)也具有全局刷新效果,并使用该值来形成正确的程序逻辑。这比使用synchronized
块更棘手,因为没有相互排除代码执行,或者,您可以将其视为仅限于单个读取,写入或cas操作的互斥。
答案 1 :(得分:0)
本身没有 flush ,它更容易以这种方式思考(也更容易绘制);这就是为什么网上有很多资源引用刷新到主内存(RAM假设)的原因,但实际上并不常见。真正发生的是对负载和/或存储缓冲区执行排放到L1缓存(在IBM的情况下为L2),并且它可以通过缓存一致性协议从那里同步数据;或者换句话说,缓存足够聪明,可以相互通信(通过总线),而不是始终从主存储器中获取数据。
这是一个复杂的主题(免责声明:尽管我尝试对此进行大量阅读,但是当我有时间进行大量测试时,我绝对不能完全理解它),它是关于潜在的编译器/ cpu / etc重新排序(程序顺序永远不会被尊重),它关于缓冲区的刷新,内存障碍,释放/获取语义...我不认为你的问题是在没有博士报告的情况下回答;这就是JLS
中有更高层的原因 - "发生在"之前。
至少理解上面的一小部分,你会明白你的问题(至少前两个),没有多大意义。
退出同步块
后,真正刷回内存的是什么
可能什么都没有 - 缓存"谈话"彼此同步数据;我只能想到另外两种情况:当你第一次读取一些数据和线程死亡时 - 所有写入的数据都会被刷新到主存储器中(但我不确定)。
在这种情况下,在重新关系之前发生了什么?什么不会在进入块时从内存中重新读取什么?
真的,与上面相同的句子。
锁定如何实现此功能
通常通过引入记忆障碍;就像挥发物一样。