在阅读了一些关于java内存模型和同步的内容之后,出现了一些问题:
即使线程1同步写入,然后虽然写入的效果将刷新到主存储器,但线程2仍然看不到它们,因为读取来自1级高速缓存。因此,同步写入仅防止写入时发生冲突。 (Java thread-safe write-only hashmap)
其次,当同步方法退出时,它会自动与同一对象的同步方法的任何后续调用建立先发生关系。这可以保证对所有线程都可以看到对象状态的更改。 (https://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html)
第三个网站(我再也找不到了,对不起)说,当方法离开同步块并确定发生时,对任何对象的每次更改 - 它都不关心引用的来源 - 将被刷新到内存中 - 在情况之前。
我的问题是:
如果我认为所有内容都将被重新读取和刷新是正确的,那么这可以通过在锁定和解锁函数中使用synchronized-block来实现(对于大多数情况下也是如此),对吗?如果这是错的,如何实现这一功能?
先感谢您!
发生在关系之前的事情是你必须要理解的基本事情,因为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
s内部使用的那样)也具有全局刷新效果,并使用该值来形成正确的程序逻辑。这比使用synchronized
块更棘手,因为没有相互排除代码执行,或者,你可以看到它具有限制为单个读,写或cas操作的互斥。
没有冲洗本身,这样更容易思考(更容易绘制);这就是为什么有很多在线资源引用刷新到主内存(RAM假设),但实际上它并不经常发生。真正发生的是对负载和/或存储缓冲区执行排放到L1缓存(在IBM的情况下为L2),并且由高速缓存一致性协议来同步来自那里的数据;或者换句话说,缓存足够聪明,可以相互通信(通过总线),而不是始终从主存储器中获取数据。
这是一个复杂的主题(免责声明:尽管我尝试对此进行大量阅读,但是当我有时间进行大量测试时,我绝对不能完全理解它),这是关于潜在的编译器/ cpu /等等。 -orderings(程序顺序永远不会被尊重),它是关于缓冲区的刷新,关于内存障碍,释放/获取语义......我不认为你的问题在没有博士报告的情况下是可以回答的;这就是为什么在JLS
中有更高层称为“发生在之前”。
至少理解上面的一小部分,你会明白你的问题(至少前两个),没有多大意义。
通过退出synchronized块,真正刷回内存的是什么
可能什么都没有 - 缓存彼此“交谈”以同步数据;我只能想到另外两种情况:当你第一次读取一些数据和线程死亡时 - 所有写入的数据都将被刷新到主存储器(但我不确定)。
在这种情况下,关系之前发生了什么?什么不会在进入块时从内存中重新读取什么?
真的,和上面一样的句子。
锁如何实现此功能
通常通过引入记忆障碍;就像挥发物一样。