4

很抱歉这个非技术性的标题,但我认为它很好地总结了我的问题。如果我正确解释了我读过的内容,同步块(除了其他后果)将使所有变量更新到主内存/从主内存更新(即使是那些在同步块内没有显式访问的变量,只有它们的“父”?) . 例如引用这个stackoverflow问题的答案(我把它断章取意,我稍后会回到它):

内存屏障适用于所有内存引用,甚至是不相关的内存引用。

我需要确认我是否正确解释了这一点。我有 2 个线程(线程 A、线程 B)。考虑以下代码:

public class SomeClass {

private final Object mLock = new Object();
private int[] anArray;

public void initA() {
  synchronized(mLock) {
     ...
     anArray = new int[...];
     operationA();
  }
}

public void operationA() {
  synchronized(mLock) {
      // Manipulating the ELEMENTS of anArray,
      // e.g. in loops, etc.
      anArray[i] = ...
  }
}

public int[] getterB() {
   synchronized(mLock) {
      return anArray;   
   }
}
}

getterB()ThreadB调用,从initA()ThreadA调用。(请注意,甚至在创建ThreadB之前调用它,因此只有和是并发的。)还要注意,我有充分的理由不返回数组的副本(不,threadB不想更改其元素;原因是我的软件的外部要求,现在不相关)。operationA()initA()getterB()operationA()getterB()

线程B这样做:

int[] anArray = aSomeClass.getterB(); // aSomeClass is an instance of SomeClass
if (anArray[i] == n) { ....... } // various operations
...
//  various other operations that read the elements of anArray

如您所见,在内存屏障中getterB()anArray访问引用,而不是数组值本身。我的问题:

  1. threadB 会看到最新的数组元素值吗?(即元素本身是否也从主内存更新getterB()?)

  2. 引用的声明提到不相关的缓存副本也从主内存更新。我不是 100% 如何解释这个不相关的(与用于锁定的变量无关?或与整个同步块无关?)。我知道我把引用断章取义了,因为这是一个不同的 stackoverflow 问题,所以我在那里添加了评论。所以我很感激我的这个问题是否在那里(或这里——我不在乎)得到了回答。

  3. anArray如果是对象数组(而不是原始类型),答案有什么不同吗?更进一步,如果它不是一个数组,而是一个包含对其他类的引用的类怎么办?(即引用其他对象的对象,通过返回的对象访问包含的对象getterB())。threadB 会使用 这些包含引用的最新副本,还是使用它自己的本地缓存副本(因为getterB()只更新了它们的容器对象,而不是包含的引用本身)?

4

1 回答 1

4

按顺序回答您的问题:

  1. 是的:您可以放心地假设从任何先前调用的任何修改的所有值operationA()都将在getterB().

  2. 我会在另一个链接中回答这个问题;我承认我还没有阅读那个链接。但我的理解是,当您进入和退出同步块时,所有挂起的内存写回都将“有效”发生(尽管这是如何发生的细节 - 即效率,以及是否有更多的缓存/流水线让它看起来那样的“技巧”——将取决于硬件和编译器)。如果您想了解更多详细信息,我曾经发现此链接很有用:http: //www.infoq.com/articles/memory_barriers_jvm_concurrency

  3. 不,没有区别(鉴于我在答案 2 中写的)。

最后,只是一个评论,我会对上面总结的代码非常警惕,因为getterB()它不会返回数组的副本。我了解您以上述方式这样做是有原因的(并且您不想要这种反馈!),但是您应该确保了解线程中的所有“各种操作”都B发生anArray在之后getterB()已返回将不受保护。(换句话说,A在此期间对线程上的数组所做的任何更改都会有风险。)避免低效的深度数组复制的一种替代方法是将这些“各种操作”移动到新方法中的同步块中。SomeClass并摆脱getterB()完全。当然,我意识到代码中的“正确解决方案”取决于此处未显示的许多内容,因此请随意忽略这一点。

于 2012-07-01T01:45:47.680 回答