Java的同步会更新整个缓存,还是只有我同步的对象?

11
如果我在同步方法或同步块内访问一个对象,那么该访问元素中的所有对象是否也都被同步了?
假设有一个对象Queue,其中具有同步的add()和take()方法,接收并处理复杂对象Thing。Thing中有许多列表,其中包含其他不同的对象。
现在想象线程Before创建Thing,并将一些现有对象放入Thing中,修改其中一些对象等等。Before线程将Thing添加到Queue中。稍后,线程After从Queue中取出Thing。
问题:Thing及其所有子/子对象是否与Before留下的状态相同?即使线程After可能早些时候正在处理其中一个子元素?因为我想像线程After的处理器仍然可能具有有关该子元素的某些缓存信息(该子对象的地址仍然相同)。只有通过以同步方式访问父对象Thing才能使所有这些缓存的东西无效?
2个回答

9
如果一个线程修改了一个变量,除非以下情况(至少在以下情况下;我不能100%确定是否还有更多情况)另一个线程无法保证看到这些变化:

  • 修改线程离开同步块或方法;这引起了线程缓存的刷新(同样,进入同步块或方法的线程也会引起刷新)--这是您的情况发生的事情
  • 修改的变量被声明为volatile,或者它是来自java.util.concurrent.atomic的原子变量之一
  • 修改线程完成(这也引起了刷新,同样地,线程的启动引起了刷新)

因此,如果您按照您所解释的进行同步,其他线程将看到所有更改。


1
是的,我知道那个,但那不完全是我的问题 :-) 同步也必须影响处理器缓存,以便更改被刷新到公共内存,这就是我的问题所在。我知道如果您从多个并行线程中访问引用而没有经过同步块,它会使一切混乱。但这更多地涉及将东西从一个线程传递到另一个线程,并确保数据结构仍然正确。明白我的意思吗? - Franz Kafka
1
请参考保罗的答案。 阻塞线程只是同步的一个方面,先行发生关系(刷新缓存)与其同样重要,甚至更重要。 这也更难理解,并且API对happens-before有些不清晰的地方(例如,我认为SwingUtilities.invokeLater会引发一种happens-before,但我不确定100%)。 我强烈推荐阅读Goetz等人的《Java并发实践》。 他们非常强调happens-before。 - toto2
谢谢你没有解释就给我点踩... @Franz: 我没有意识到你在问内存模型。让我重新考虑我的答案。 - musiKk
@toto2:很好。这个问题对我来说听起来确实不同。希望我现在理解了它。 :) - musiKk

9
Java内存模型中的重要概念是 happens-before 顺序。由 happens-before 读操作之前发生的写操作的结果对这些读操作是可见的。其他结果可能可见,也可能不可见。 happens-before 顺序是通过线程之间的操作同步顺序以及单个线程内操作的自然顺序引起的。
如果您在 Before 上对对象(例如,您的 Queue )进行同步,并在此同步块内或之前执行 Thing 及其“子对象”的所有操作,并在同步块后执行 After 对相同的 Queue 进行同步并读取这些对象,则所有这些更改 After 是可见的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接