Java 中 synchronized 关键字的记忆效应

Memory effects of synchronized keyword in Java

这个问题之前可能已经有人回答过,但由于问题的复杂性,我需要确认一下。所以我改一下问题

问题1 : 当一个线程进入一个同步块时,内存屏障会包括所有触及的字段,而不仅仅是我同步的对象的字段?因此,如果在同步块内修改了许多对象,那么线程内存缓存之间就会有大量内存移动。

Thread 1
object.field1 = "";
synchronized (lock) {
  farAwayObject.field1 = "";
  farAwayObject.evenFarther.field2 = "";
}

Thread 2. assuming thread ordering is correct
synchronized (lock) {
  //thread 2 guaranteed to see all fields above as ""
  //even object.field1 ?
}

问题 2:线程 1 中的 object.field1 = ""; 是 happens-before 关系的隐含部分吗?

我希望是,但可能不是。如果没有,是否有技巧可以在不将其放入同步块的情况下做到这一点?否则很难在程序上推理 并且将所有内容都放在 synchronized { } 下是不切实际的。

编辑:澄清:object.field1 不是易变的,问题是 "will thread 2 guaranteed to see the write of thread 1, at least "。我的问题是关于内存可见性。为了论证,假设只有线程 1 写入非易失性 object.field1。

问题 2 可以改写

"Will a synchronized block on a lock push changes made before to be seen by other threads synchronizing on the same lock? "

When a thread enters a synchronized block, the memory barrier will include any fields touched, not just fields of the object that I synchronized on

假设 farAwayObjectevenFarther 的字段总是通过在应用程序周围的同一对象上获取 lock 来修改和访问,所有线程将始终看到更新farAwayObjectevenFarther 因为 synchronized 强制执行 happens-before 条件。

//thread 2 guaranteed to see all fields above as ""

如果不知道 object.field1 是如何声明的,就不能这样说。假设 field1 是未标记为 volatile 的引用,它不会成为 happens before 关系的一部分,线程可能会看到它的陈旧值。

I hope it is but It might not. If not is there a trick to make it so without putting it into the sync block?

是的。将 object.field1 标记为 volatile


正在处理您的编辑:

The question 2 can be rephrased as

"Will a synchronized block on a lock push changes made before to be seen by other threads synchronizing on the same lock? "

据我所知,答案是肯定的,前提是写入线程在读取线程之前获取锁。不幸的是,这是您通常无法保证的事情,这就是为什么 object.field1 需要标记为 volatile 或语句需要移动到 synchronized 块内的原因。

看看 JSR 133,它讨论了当线程 退出 一个 syncronized 块时缓存被刷新到主内存。这应该进一步澄清事情。


1) When a thread enters a synchronized block, the memory barrier will include any fields touched, not just fields of the object that I synchronized on?

正确。 (假设线程1和线程2在同一个锁上同步。)

So if many many objects are modified inside a synchronized block, that's a lot of memory moves between thread memory caches.

可能是的。但是,它(可能)不是缓存之间的移动。更有可能的是,它是从一个处理器的高速缓存到内存的移动,以及从内存到第二个处理器的高速缓存的移动。当然,这取决于硬件如何实现内存层次结构。

2) Is object.field1 = ""; in thread 1 implicitly part of the happens-before relationship?

存在 happens-before 关系链

  1. 写入 object.field1 发生在获取锁之前。
  2. 这发生在写入 farAwayObject 等之前。
  3. 这发生在线程 1 释放锁之前
  4. 这发生在线程 2 获取锁之前
  5. 这发生在线程 2 读取 object.field1 之前。

问题是,如果在 object.field1 被线程 1 或其他线程获取 lock 之前,存在对 object.field1 的干预写入,会发生什么情况。在任何一种情况下,happens-before 链都不足以确保线程 2 看到线程 1 写入的值。

the memory barrier will include any fields touched, not just fields of the object that I synchronized on?

是的。

Is object.field1 = ""; in thread 1 implicitly part of the happens-before relationship?

是的,即使它不是易变的。


The happens-before order is a partial order.

The happens-before order is given by the transitive closure of synchronizes-with edges and program order. It must be a valid partial order: reflexive, transitive and antisymmetric.

(JLS 17.4.7)

同步边沿之前的动作(即同步锁的释放)按程序顺序排序,因此与释放同步。传递性表示,由另一个线程中同一锁的 acquire 排序的操作因此具有 happens-before 顺序,同时释放该锁和释放之前的操作该锁,无论它是否在 synchronized 块的主体内。关于这一点要记住的重要一点是,排序发生在操作上(即锁的 Acquire/release),而不是同步关键字括号中暗示的块。括号表示 acquire/release 动作的位置以及一组动作不能交错的位置。

最后,请记住 happens-before 是“部分”订单。意思是:

  1. Happens before 当操作碰巧以特定顺序出现时(即 release/acquire、write/read 等)强制执行内存一致性
  2. 发生在之前取决于更强的保证,例如产生正确功能的程序顺序。
  3. 发生在之前并不能防止非原子操作交错引起的错误
  4. happens before是action与强action之间的传递关系。 (您可以将共享变量的读取放在 synchronized 块之外,只要写入发生在块之前并且读取发生在块之后)。更强大的排序保证也发生在排序之前,但它们还提供规范中描述的额外效果。