首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >JMM保证了对对象的最终作为字段和非最终引用。

JMM保证了对对象的最终作为字段和非最终引用。
EN

Stack Overflow用户
提问于 2017-01-31 10:34:42
回答 1查看 346关注 0票数 7

我试着理解最后一个领域的语义。

让研究代码:

代码语言:javascript
复制
public class App {

    final int[] data;
    static App instance;

    public App() {
        this.data = new int[]{1, 0};
        this.data[1] = 2;
    }


    public static void main(String[] args) {
        new Thread(new Runnable() {
            public void run() {
                instance = new App();
            }
        }).start();

        while (instance == null) {/*NOP*/}
        System.out.println(Arrays.toString(instance.data));
    }
}

我有一些问题:

  1. jmm是否保证,如果应用程序终止,那么它会输出1,2?
  2. 在循环终止后,jmm是否保证instance.data不为空?

P.S.我不知道如何使标题正确,请随时编辑。

附加内容

如果我们替换:

代码语言:javascript
复制
public App() {
    this.data = new int[]{1, 0};
    this.data[1] = 2;
}

使用

代码语言:javascript
复制
public App() {
    int [] data = new int[]{1, 0};
    data[1] = 2;
    this.data = data;    
}

另外,我想知道在我的示例中,如果用final替换易失性,wjat将是什么样子。

因此,我想得到关于4个新案例的解释。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-02-01 15:14:41

是的,带着渔获物。您将在循环之后重新读取instance变量,而且由于这两种读取都是动态的,退出循环并不能保证循环后读取非null引用。

由于这个问题不是问题的主题,请进行以下更改:

代码语言:javascript
复制
App instance;
while((instance=App.instance) == null) {/*NOP*/}
System.out.println(Arrays.toString(instance.data));

然后,如果应用程序终止,它将输出[1,2]。问题是,final字段语义适用于整个构造函数,数组引用写入该字段的确切时间与此无关。这还意味着,在构造函数中,重新排序是可能的,因此,如果this引用在构造函数完成之前转义,则所有保证都是无效的,无论this是在程序顺序写入之前还是之后转义。因为在您的代码中,this不会在构造函数完成之前转义,所以应用了保证。

请参阅字段语义

当构造函数完成时,对象被认为是完全初始化的。只有在对象被完全初始化后才能看到对对象的引用的线程,保证看到该对象的final字段的正确初始化值。

注意,它引用的是完全初始化的状态,而不是对特定final字段的写入。这一点也将在下一节第17.5.1节中讨论。

设o是对象,c是o的构造函数,其中写入了final字段f。当c正常或突然退出时,o的final字段f会发生冻结作用。

如果将变量更改为volatile,则几乎没有任何保证。volatile字段建立了对该变量的写入与后续读取之间的关系之前发生的情况,但经常被忽略的关键点是“后续”一词。如果App实例发布不当,如您的示例中所示,则无法保证主线程对instance.data的读取将是后续的。如果它读取null引用(这现在是可能的),那么您就知道它不是后续的。如果它读取一个非null引用,您知道它是字段写入之后的,这意味着您保证在第一个槽中读取1,但是在第二个槽中,您可以读取02

如果您想从障碍和重新排序的角度来讨论这一点,volatile写到data保证所有以前的写入都已提交,这包括将1写入到第一个数组槽,但它不能保证后续的非volatile写入不会提前提交。因此,仍然有可能在App写入之前执行不适当的volatile引用发布(尽管这种情况很少发生)。

如果将写入移至构造函数的末尾,则一旦看到非null数组引用,所有以前的写入都是可见的。对于final字段,不需要进一步讨论,如上所述,构造函数中写入的实际位置与此无关。对于volatile情况,如前所述,不能保证读取非null引用,但是当您读取它时,所有以前的写入都已提交。知道new int[]{1, 0};表达式被编译成相当于hiddenVariable=new int[2]; hiddenVariable[0]=1; hiddenVariable[1]=0;的表达式可能会有所帮助。将另一个数组写入放在其构造之后,但在数组引用到字段的volatile写入之前,不会更改语义。

票数 9
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/41955348

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档