同步块:变量“内部”更新其他变量

时间:2012-06-30 23:34:28

标签: java android synchronized-block

很抱歉非技术名称,但我认为它很好地总结了我的问题。如果我解释了我正确阅读的内容,同步块(除了其他后果)将使所有变量更新到主存储器(或者从同步块内未明确访问的那些变量,只有它们的“父”?) 。例如。引用this stackoverflow问题的答案(我把它脱离了上下文,我稍后会再回过头来):

  

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

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

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;   
   }
}
}
ThreadB 调用

getterB(),从{em> ThreadA 调用initA()operationA()。 (请注意,即使在创建 ThreadB 之前,也会调用initA(),因此只有getterB()operationA()是并发的。)另请注意,我有充分的理由不这样做在getterB()中返回数组的副本(不, threadB 不希望更改其元素;原因是我的软件的外部要求现在不相关。)

threadB执行此操作:

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问题,我添加了一条评论there。所以,如果我的问题得到解答(或者在这里 - 我不在乎),我感激不尽。

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

1 个答案:

答案 0 :(得分:4)

按顺序提出问题:

  1. 是的:您可以放心地假设从operationA()的结果引用的数组中所有已修改的值getterB()中的所有值都是“最新的”

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

  3. 不,没有区别(考虑到我在答案2中写的内容)。

  4. 最后,由于getterB()没有返回数组副本这一事实,我只是对上面概述的代码非常警惕。我知道你有理由以上述方式这样做(并且你不想要这种反馈!),但是你应该确保理解线程{{1}中的所有“各种操作”在B返回后发生的anArray上的}将不受保护。 (换句话说,在此期间对线程getterB()上的数组所做的任何更改都会有危险。)避免低效深度数组复制的另一种方法是在同步块中移动这些“各种操作”在A中的新方法中,完全摆脱SomeClass。当然,我意识到代码中的“正确解决方案”依赖于此处未显示的许多内容,因此请随意忽略这一点。