10

我试图准确了解元素可见性如何在 java 中的数组上起作用。

给定班级:

class IntList {

    private final int[] array;

    public IntList(int[] array) {
        this.array = array;
    }

    public int[] readElements() {
        return Arrays.copyof(this.array, this.array.length);
    }

}    

以及以下用于创建实例的方法体:

int[] array = new int[length];
fillArrayWithRandomData(array); // puts data into the array from arbitrary source
return new IntList(array);

我想知道 中的元素IntList是否保证被其他获得对返回的引用的线程可见IntList

我确信对数组的引用将是可见的,因为它是最终的,但我似乎无法保证数组中的元素也可见。

注意:IntList该类没有允许修改数组的方法,并且数组引用不会发布到任何其他对象,我只是想知道构造后的可见性。

编辑:对不起,我的课程String在我的实际实现中没有被调用。我将班级名称更改为,IntList因为似乎有太多的混乱。

编辑: 我要在这里给出的最终答案是是的,元素是可见的。
@MikeClark 找到了 JLS 的答案:JLS § 17.5 “最终字段的使用模型很简单:在该对象的构造函数中设置对象的最终字段;并且不要将正在构造的对象的引用写在另一个线程可以在对象的构造函数完成之前看到它。如果遵循这个,那么当另一个线程看到该对象时,该线程将始终看到该对象的最终字段的正确构造版本。它还将看到任何对象的版本或由至少与最终字段一样最新的最终字段引用的数组。”

再次感谢!

4

3 回答 3

2

由于您在构造函数中填充数组,所以是的,任何调用new String(int[] array)都会在它返回时初始化数组。该final关键字还将保证array在分配时对参数的最新更改将是可见的。

于 2012-11-20T19:48:50.780 回答
0

我发现这很有帮助http://jeremymanson.blogspot.ch/2009/06/volatile-arrays-in-java.html 所以基本上一切都发生在 volatile 读取保证被其他线程看到之前。所以有一些技巧可以让它发生。此外,jaa 提供原生 AtomicReference/Long/Integer/...Array 支持。这将确保更新对其他线程可见。

于 2016-12-20T05:36:54.280 回答
-2

您的问题似乎混淆了许多不同的概念。

  • 能见度和final彼此无关
  • 可见性和线程彼此无关
  • 变量在private类外部对任何其他代码都不可见,无论它是否是最终的
  • final数组仍然可以更改其元素。它只是对数组本身的引用final

当您构造IntList对象时(顺便说一句,感谢您更改名称),如下所示:

public IntList(int[] array) {
    this.array = array;
}

内部this.array字段引用传递给构造函数的相同数组对象。仍然可以从String类外部修改数组:

int[] array = {1, 2, 3};
IntList list = new IntList(array);
System.out.println(Arrays.toString(list.readElements()); // prints [1, 2, 3]
array[1] = 0;
System.out.println(Arrays.toString(list.readElements()); // prints [1, 0, 3]

您可以通过在构造函数中制作数组的副本来将您的类与此隔离:

public IntList(int[] array) {
    this.array = Arrays.copyOf(array, array.length);
}
于 2012-11-20T19:51:08.503 回答