Java

时间:2017-02-08 07:26:11

标签: java multithreading java-memory-model

之前可能已经回答过,但由于问题的复杂性,我需要确认。所以我重新解释了这个问题

问题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 = "";隐含地是发生在之前关系的一部分吗?

我希望是,但可能没有。如果不是,有一个技巧,使它没有把它放入同步块?否则很难在程序上推理 将所有内容置于同步{}。

之下是不切实际的

编辑:澄清:object.field1不易变,问题是"线程2保证看到线程1的写入,至少"。我的问题是内存可见性。为了论证,让我们说只有线程1写入非易失性object.field1。

问题2可以改为

"锁定推送上的同步块是否会在同一个锁上同步的其他线程看到之前发生变化? "

3 个答案:

答案 0 :(得分:4)

  

1)当线程进入同步块时,内存屏障将包括所触及的任何字段,而不仅仅是我同步的对象的字段?

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

  

因此,如果在同步块中修改了许多对象,那么在线程内存缓存之间会有很多内存移动。

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

  

2)是object.field1 =“”;在线程1隐含地部分发生在之前的关系?

有一系列先发生过关系

  1. 在获取锁定之前发生对object.field1的写入。
  2. 在写入farAwayObject之前发生这种情况。依此类推。
  3. 在线程1释放锁定之前发生
  4. 在线程2获取锁定之前发生
  5. 在线程2读取object.field1之前发生。
  6. 如果在线程1获取object.field1之前或者通过某个其他线程对lock进行插入写入,则会出现问题。在任何一种情况下,before-before链不足以确保线程2看到线程1写入的值。

答案 1 :(得分:3)

  

当线程进入同步块时,内存屏障将会出现   包括触摸的任何字段,而不仅仅是我对象的字段   

同步

假设22farAwayObject的字段始终通过在应用程序周围的同一对象上获取evenFarther来修改和加入,所有线程将始终看到对lockfarAwayObject,因为evenFarther强制发生在之前。

  

synchronized

//thread 2 guaranteed to see all fields above as ""在不知道如何宣布的情况下也不能这样说。假设object.field1是未标记为field1的引用,它不会是关系之前发生的一部分,并且线程可能会看到它的陈旧值。

  

我希望是,但可能没有。如果没有,那就有诀窍   没有把它放入同步块?

是。将volatile标记为object.field1

添加您的修改:

  

问题2可以改为

     

"锁定上的同步块是否会在之前进行更改   其他线程看到在同一个锁上同步? "

AFAIK答案是肯定的,前提是写线程在读取线程之前获取锁定。不幸的是,这通常是您无法保证的,这就是为什么volatile需要标记为object.field1语句需要在volatile块内移动的原因。< / p>

查看JSR 133,其中讨论了当线程退出 synchronized块时刷新到主内存的缓存。这应该进一步澄清事情。

答案 2 :(得分:1)

  

内存屏障将包括所触及的任何字段,而不仅仅是我同步的对象的字段?

  

是object.field1 =&#34;&#34 ;;在线程1隐含地部分发生在之前的关系?

是的,即使它不是不稳定的。

  

之前发生的订单是部分订单。

     

发生之前的顺序是由与边缘和程序顺序同步的传递闭包给出的。它必须是有效的偏序:反身,传递和反对称。

     

JLS 17.4.7

同步边缘之前的操作(即同步锁定的释放)按程序顺序排序,因此与发布同步。传递性表示由另一个线程中的同一个锁的获取所排序的操作因此具有一个先前发生的命令,包括释放该锁和释放该锁之前的操作,无论是不是它在synchronized块体内。要记住这一点的重要一点是,对动作(即获取/释放锁)的排序不是一个块,例如synchronized关键字的括号所隐含的。括号表示获取/释放操作的位置以及一组操作不能交错的位置。

最后,记住之前发生的事情是&#34;部分&#34;订购。这意味着:

  1. 在操作发生在特定顺序(即发布/获取,写入/读取等)时强制执行内存一致性之前发生
  2. 之前发生的事情取决于更强的保证,例如程序顺序,以产生正确的功能。
  3. 之前发生的事情不会阻止来自非原子行为交错的错误
  4. 之前发生的是行动和强烈行动之间的传递关系。 (只要写入到块之前并且读取在块之后),就可以将共享变量的读取放在synchronized块之外。更强的订购保证也会在订购之前发生,但它们也提供了规范中描述的其他效果。