易失性和安全性发布对象

时间:2013-01-27 09:47:28

标签: java multithreading

在一些文章中,它说使用volatile修复了Double Checked Locking问题。

class Foo {
        private volatile Helper helper = null;
        public Helper getHelper() {
            if (helper == null) {
                synchronized(this) {
                    if (helper == null)
                        helper = new Helper(); //Important
                }
            }
            return helper;
        }
    }

但是,即使我们使用volatile作为辅助字段,这怎么可能是一个安全的出版物呢? 我的意思是如何保证我们不会得到一个不一致的Helper对象?

4 个答案:

答案 0 :(得分:3)

没有volatile,因为初始if (helper == null)在同步块之外,所以没有可见性/一致性保证。特别是,helper可能不是null,而是指仅部分创建的对象。

那是因为helper = new Helper()不是原子操作:

  1. 分配一些内存
  2. 它构造对象
  3. 它将对象的引用分配给helper
  4. 如果没有同步,观察线程可以按任何顺序看到这些操作,特别是它可以在2之前看到3。

    通过使helper为volatile,您在写入和读取之间引入了发生之前的关系(由the Java Memory Model定义),这可确保如果您看到3从观察线程中,您还将看到1和2。

    特别是,在易失性写入之前执行的任何操作都将从易失性读取中可见(如果是后续的话)。

答案 1 :(得分:1)

我认为这是因为JVM保留了helper=new Helper()的顺序。我的意思是只有在创建对象之后才会进行赋值。如果我错了,请纠正我。

答案 2 :(得分:0)

从高级别的角度来看volatile保证在易失性写入之前发生的任何操作的结果(即new Helper()内的操作)对于任何其他线程都是可见的在后续的volatile读取之后(即当另一个线程在外部检查中看到helper != null时)。

从低级别的角度来看,它依赖于实现。但总的来说:

  • 应禁止可能导致使用先前操作重新排序易失性写入的编译器优化
  • JIT应该将memory barriers插入生成的代码中,以防止在CPU级别进行类似的重新排序(如果特定的CPU架构允许重新排序)

答案 3 :(得分:-1)

好的,同步部分外的检查是为了避免不必要地进入监视器部分。如果您尝试优化性能,可以省略它。只有在对象的creaton之后无法将helper重置为null时,它才有效。同步部分内部的检查是为了避免不一致(因为我们在等待进入监视器时可能已经创建了对象)。

synchronized的内部部分只能相互独立执行。这就是一致性的来源。但是又一次:只有当helper在被赋予对Helper实例的新引用后才能变为null时,才允许外部if (helper == null)