构造函数中的Java内存可见性

问题描述 投票:6回答:2

对于以下简化类:

public class MutableInteger {
    private int value;
    public MutableInteger(int initial) {
        synchronized(this) { // is this necessary for memory visibility?
            this.value = initial;
        }
    }
    public synchronized int get() {
        return this.value;
    }
    public synchronized void increment() {
        this.value++;
    }
    ...
}

我想一般的问题是同步保护的可变变量是否需要在构造函数中设置初始值时进行同步?

java memory concurrency
2个回答
3
投票

你是对的,没有构造函数中的synchronized块,对于非最终字段没有可见性保证,如this example中所示。

但实际上我宁愿在这种情况下使用volatile字段或Atomic*类。

更新:此处还必须提到,为了使您的程序成为correctly synchronized(由JLS定义),您需要以安全的方式发布对象的引用。引用的示例不会这样做,因此您可能会在非最终字段中看到错误的值。但是如果正确发布对象引用(即通过将其分配给另一个对象的final字段,或者在调用Thread.start()之前创建它),则可以保证您的对象至少与时间一样最新。发布,因此使构造函数中的synchronized块不必要。


0
投票

虽然你已经接受了答案,但我加上我的两分钱。根据我所读到的内容,同步或使字段不稳定不会影响以下可见性。

线程T1可能会看到this的非空值,但除非你使字段value为final,否则线程T1很有可能看到value的默认值。

该值可以是易失性的,也可以在同步块(监视器获取和释放)中访问,无论哪种方式,只要遵循正确的执行顺序,就会发生 - 从写入到读取value的边缘之前。对此没有任何争议。但这不是我们在这里必须考虑的边缘之前发生的事情,而是对象本身的正确发布(MutableInteger)。创建对象是双重的,JVM首先分配堆空间然后开始初始化字段。线程可以看到对象的非空引用,但只要该字段不是最终的(未假设引用已正确发布),则可以看到该对象的未初始化字段。

© www.soinside.com 2019 - 2024. All rights reserved.