java - 根据java内存模型,synchronized真正做了什么?

问题描述 投票:0回答:2

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

即使线程1同步写入,然后虽然写入的效果将刷新到主存储器,但线程2仍然看不到它们,因为读取来自1级高速缓存。因此,同步写入仅防止写入时发生冲突。 (Java thread-safe write-only hashmap

其次,当同步方法退出时,它会自动与同一对象的同步方法的任何后续调用建立先发生关系。这可以保证对所有线程都可以看到对象状态的更改。 (https://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html

第三个网站(我再也找不到了,对不起)说,当方法离开同步块并确定发生时,对任何对象的每次更改 - 它都不关心引用的来源 - 将被刷新到内存中 - 在情况之前。

我的问题是:

  1. 通过退出synchronized块真正刷回内存的是什么? (正如一些网站还说只有被锁定的对象才会被刷回。)
  2. 在这种情况下,关系之前发生了什么?什么不会在进入块时从内存中重新读取什么?
  3. 锁如何实现此功能(来自https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/Lock.html): 所有Lock实现必须强制执行与内置监视器锁提供的内存同步语义相同的内存同步语义,如Java™语言规范的第17.4节所述: 成功的锁定操作具有与成功锁定操作相同的内存同步效果。成功解锁操作具有与成功解锁操作相同的内存同步效果。不成功的锁定和解锁操作以及重入锁定/解锁操作不需要任何内存同步效果。

如果我认为所有内容都将被重新读取和刷新是正确的,那么这可以通过在锁定和解锁函数中使用synchronized-block来实现(对于大多数情况下也是如此),对吗?如果这是错的,如何实现这一功能?

先感谢您!

java concurrency synchronized memory-model
2个回答
2
投票

发生在关系之前的事情是你必须要理解的基本事情,因为the formal specification就这些而言。像“刷新”这样的术语是可以帮助您理解它们的技术细节,或者在最坏的情况下误导您。

如果一个线程在A中执行动作synchronized(object1) { … },然后在B中执行动作synchronized(object1) { … },假设object1引用相同的对象,则AB之间存在关系之前发生的关系,这些操作在访问共享时是安全的可变数据(假设没有其他人修改此数据)。

但这是一种有针对性的关系,即B可以安全地访问由A修改的数据。但是当看到两个synchronized(object1) { … }块时,确定object1是同一个对象,你仍然需要知道A是否在BB被执行之前执行A,以了解发生在关系之前的方向。对于普通的面向对象的代码,这通常是自然地工作,因为每个动作将对它找到的对象的任何先前状态进行操作。

说到冲洗,留下一个synchronized块导致所有书面数据的刷新并进入synchronized块导致重读所有可变数据,但没有相同排除保证同一实例上的synchronized,没有控制在另一个之前发生。更糟糕的是,您无法使用共享数据来检测情况,因为如果不阻塞其他线程,它仍然可能会不一致地修改您正在操作的数据。

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

因此,您可以使用对象上的同步来保护对存储在其他位置的数据的访问,即不在该对象中,但是仍然需要在同一对象实例上进行一致的同步,以便对相同的共享数据进行所有访问,从而使程序逻辑复杂化简单地同步包含受保护数据的同一对象。


如果线程正在读取和写入相同的volatile变量,那么Lock变量(如volatiles内部使用的那样)也具有全局刷新效果,并使用该值来形成正确的程序逻辑。这比使用synchronized块更棘手,因为没有相互排除代码执行,或者,你可以看到它具有限制为单个读,写或cas操作的互斥。


0
投票

没有冲洗本身,这样更容易思考(更容易绘制);这就是为什么有很多在线资源引用刷新到主内存(RAM假设),但实际上它并不经常发生。真正发生的是对负载和/或存储缓冲区执行排放到L1缓存(在IBM的情况下为L2),并且由高速缓存一致性协议来同步来自那里的数据;或者换句话说,缓存足够聪明,可以相互通信(通过总线),而不是始终从主存储器中获取数据。

这是一个复杂的主题(免责声明:尽管我尝试对此进行大量阅读,但是当我有时间进行大量测试时,我绝对不能完全理解它),这是关于潜在的编译器/ cpu /等等。 -orderings(程序顺序永远不会被尊重),它是关于缓冲区的刷新,关于内存障碍,释放/获取语义......我不认为你的问题在没有博士报告的情况下是可以回答的;这就是为什么在JLS中有更高层称为“发生在之前”。

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

通过退出synchronized块,真正刷回内存的是什么

可能什么都没有 - 缓存彼此“交谈”以同步数据;我只能想到另外两种情况:当你第一次读取一些数据和线程死亡时 - 所有写入的数据都将被刷新到主存储器(但我不确定)。

在这种情况下,关系之前发生了什么?什么不会在进入块时从内存中重新读取什么?

真的,和上面一样的句子。

锁如何实现此功能

通常通过引入记忆障碍;就像挥发物一样。

© www.soinside.com 2019 - 2024. All rights reserved.