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
假设 farAwayObject
和 evenFarther
的字段总是通过在应用程序周围的同一对象上获取 lock
来修改和访问,所有线程将始终看到更新farAwayObject
和 evenFarther
因为 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 关系链
- 写入
object.field1
发生在获取锁之前。
- 这发生在写入
farAwayObject
等之前。
- 这发生在线程 1 释放锁之前
- 这发生在线程 2 获取锁之前
- 这发生在线程 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.
同步边沿之前的动作(即同步锁的释放)按程序顺序排序,因此与释放同步。传递性表示,由另一个线程中同一锁的 acquire 排序的操作因此具有 happens-before 顺序,同时释放该锁和释放之前的操作该锁,无论它是否在 synchronized
块的主体内。关于这一点要记住的重要一点是,排序发生在操作上(即锁的 Acquire/release),而不是同步关键字括号中暗示的块。括号表示 acquire/release 动作的位置以及一组动作不能交错的位置。
最后,请记住 happens-before 是“部分”订单。意思是:
- Happens before 当操作碰巧以特定顺序出现时(即 release/acquire、write/read 等)强制执行内存一致性
- 发生在之前取决于更强的保证,例如产生正确功能的程序顺序。
- 发生在之前并不能防止非原子操作交错引起的错误
- happens before是action与强action之间的传递关系。 (您可以将共享变量的读取放在
synchronized
块之外,只要写入发生在块之前并且读取发生在块之后)。更强大的排序保证也发生在排序之前,但它们还提供规范中描述的额外效果。
这个问题之前可能已经有人回答过,但由于问题的复杂性,我需要确认一下。所以我改一下问题
问题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
假设 farAwayObject
和 evenFarther
的字段总是通过在应用程序周围的同一对象上获取 lock
来修改和访问,所有线程将始终看到更新farAwayObject
和 evenFarther
因为 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 关系链
- 写入
object.field1
发生在获取锁之前。 - 这发生在写入
farAwayObject
等之前。 - 这发生在线程 1 释放锁之前
- 这发生在线程 2 获取锁之前
- 这发生在线程 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.
同步边沿之前的动作(即同步锁的释放)按程序顺序排序,因此与释放同步。传递性表示,由另一个线程中同一锁的 acquire 排序的操作因此具有 happens-before 顺序,同时释放该锁和释放之前的操作该锁,无论它是否在 synchronized
块的主体内。关于这一点要记住的重要一点是,排序发生在操作上(即锁的 Acquire/release),而不是同步关键字括号中暗示的块。括号表示 acquire/release 动作的位置以及一组动作不能交错的位置。
最后,请记住 happens-before 是“部分”订单。意思是:
- Happens before 当操作碰巧以特定顺序出现时(即 release/acquire、write/read 等)强制执行内存一致性
- 发生在之前取决于更强的保证,例如产生正确功能的程序顺序。
- 发生在之前并不能防止非原子操作交错引起的错误
- happens before是action与强action之间的传递关系。 (您可以将共享变量的读取放在
synchronized
块之外,只要写入发生在块之前并且读取发生在块之后)。更强大的排序保证也发生在排序之前,但它们还提供规范中描述的额外效果。