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

Posted

技术标签:

【中文标题】Java 的同步会更新完整的缓存,还是只更新我同步的对象?【英文标题】:Will Java's synchronization update the complete cache, or only the object I synchronized on? 【发布时间】:2011-11-05 10:24:17 【问题描述】:

如果我访问同步方法或同步块中的对象,该访问元素中的所有对象是否也同步?

假设有一个对象Queue 具有同步的add()take() 方法,接受并分发复杂对象ThingThing 有很多列表,其中包含其他不同的对象。

现在图像线程Before 创建Thing 并将一些现有对象放入Thing,修改其中一些对象等等。 Before 线程将Thing 添加到Queue。稍后线程AfterQueue 获取Thing

问题:Thing 及其所有子对象/子对象与Before 留下的状态是否相同?即使线程After 可能更早地处理这些子元素之一?因为我对线程After 的处理器进行了映像,所以可能仍然有一些关于该子元素的缓存信息(该子对象的地址仍然相同)。所有这些缓存的东西只有通过同步访问父对象Thing才会失效?

请不要给出使用并发库之类的答案。我想了解发生了什么。

【问题讨论】:

【参考方案1】:

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

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

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

【讨论】:

是的,我知道,但这不是我的问题 :-) 同步还必须影响处理器缓存,以便将更改刷新到公共内存,这就是我的问题所在。我知道,如果您在不通过多个并行线程的同步块的情况下访问引用,那么一切都会搞砸。但这更多是关于将内容从一个线程传递到另一个线程并确保数据结构仍然正确。明白我的意思吗? 查看 Paulo 的回答。阻塞线程只是同步的一个方面,happens-before 关系(刷新缓存)同样重要,甚至更多。理解起来也很棘手,并且 API 不清楚发生在之前(例如,我认为 SwingUtilities.invokeLater 会引发发生之前,但我不是 100% 确定)。我强烈推荐 Goetz 等人的 Java Concurrency in Practice。他们非常强调发生在之前。 感谢您在没有解释的情况下投票... @Franz:对我来说,您询问的是内存模型并不明显。让我重新考虑一下我的答案。 @toto2:很公平。这个问题对我来说听起来真的很不一样。我希望我现在明白了。 :)【参考方案2】:

Java 内存模型中的重要概念是happens-before order。 在读取操作之前发生的写入操作的结果对这些读取操作是可见的。其他结果可能可见也可能不可见。

happen-before 顺序是由线程间动作的同步顺序以及各个线程中的自然动作顺序引起的。

如果您在Before 上同步一个对象(例如您的Queue),并在此同步块内部或之前对Thing 及其“子对象”进行所有操作,并且After 同步于相同的Queue 并在同步块中或之后读取这些对象,然后所有这些更改After 可见。

【讨论】:

以上是关于Java 的同步会更新完整的缓存,还是只更新我同步的对象?的主要内容,如果未能解决你的问题,请参考以下文章

数据库缓存的几种方式

如何保证Redis缓存与数据库的一致性?

rayns数据同步

Java并发编程:Java中的锁和线程同步机制

react的setState是异步还是同步

缓存同步问题