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

时间:2015-11-13 22:13:04

标签: java memory concurrency

对于以下简化类:

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++;
    }
    ...
}

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

2 个答案:

答案 0 :(得分:3)

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

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

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

答案 1 :(得分:0)

尽管您已经接受了答案,但让我加两分钱。 根据我所读的内容,同步或使字段变得不稳定将不会授予以下可见性。

线程T1可能会看到this的非空值,但是除非您将字段value设置为final,否则线程T1很有可能会看到默认值{{1 }}。

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