我有一个关于Java内存模型的问题。下面是一个简单的类,展示了这个问题:
public class ImmutableIntArray {
private final int[] array;
public ImmutableIntArray() {
array = new int[10];
for (int i = 0; i < 10; i++) {
array[i] = i;
}
}
// Will always return the correct value?
public int get(int index) {
return array[index];
}
}据我所知,JMM保证final字段的值在构造后对其他线程可见。但我希望确保其他线程在构造后能看到存储在数组中的最新版本的数据。
当然上面的代码只是一个简单的例子来说明这个问题,实际上我想为直接的字节缓冲区实现一个简单的缓存,我不想依赖一些Collection类。目前,我正在使用ReentrantReadWriteLock来确保正确的行为,但如果可能的话,我希望避免使用它。
发布于 2011-07-09 00:06:42
你的例子不太正确。为了获得最终的现场保证,您需要:
public ImmutableIntArray() {
int tmparray = new int[10];
for (int i = 0; i < 10; i++) {
tmparray[i] = i;
}
array = tmparray;
}发布于 2011-07-09 00:17:11
我确实认为,数组的语义与对象的最终引用具有相同的语义。规格说明
一个线程只能在对象完全初始化后才能看到对该对象的引用,它可以保证看到该对象的最终字段的正确初始化值。
它还说
它还将看到那些最终字段引用的任何对象或数组的版本,这些最终字段至少与最终字段一样是最新的。
http://java.sun.com/docs/books/jls/third_edition/html/memory.html#17.5
发布于 2014-12-12 22:24:13
我认为您的数组更改将在您的ImmutableIntArray中可见。根据我对JLS的阅读,冻结操作应该在构造器退出时发生。我认为临时数组的使用是无用的:
int tmparray = new int[10];
for (int i = 0; i < 10; i++) {
tmparray[i] = i;
}
array = tmparray;为了获得最终的字段保证,我们需要在构造器退出之前的某个地方冻结:
int tmparray = new int[10];
for (int i = 0; i < 10; i++) {
tmparray[i] = i;
}
array = tmparray;
[freeze]不管怎样,freeze会打开门来重新排序上面的指令,所以我们会得到同样的结果:
int tmparray = new int[10];
array = tmparray;
for (int i = 0; i < 10; i++) {
tmparray[i] = i;
}
[freeze]freeze被实现为至少包含一个StoreStore。此StoreStore屏障必须在发布构造的实例之前发布。
来自JSR-133 Cookbook
您不能将构造函数内的finals存储区下移到构造函数外部的存储区下,这可能会使该对象对其他线程可见。(如下所示,这可能还需要发布一个屏障)。类似地,您不能对前两个中的任何一个进行重新排序,第三个赋值为: v.afield = 1;x.finalField = v;...;sharedRef = x;
我认为这是由(JSR-133 Cookbook)完成的
在所有存储之后,但在从任何构造函数返回任何带有StoreStore字段的类之前,都会发出最终屏障。
所以我们不能在所有其他建筑商店完成之前在sharedRef中进行存储。
您可以在(JSR133 spec)中通过:“来自最终字段的传递保证”进行搜索。
https://stackoverflow.com/questions/6626079
复制相似问题