0

在几篇文章中,它说使用 volatile 修复了双重检查锁定问题。

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

4 回答 4

3

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

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

  1. 它分配一些内存
  2. 它构造对象
  3. 它将对对象的引用分配给helper

如果没有同步,观察线程可以以任何顺序看到这些操作,特别是它可以在 2 之前看到 3。

通过helper设置 volatile,您在写入和读取之间引入了先发生关系(由Java 内存模型定义),这确保了如果您从观察线程中看到 3,您也会看到 1 和 2。

特别是,在 volatile 写入之前执行的任何操作都将从 volatile 读取中可见(当然,如果它是后续的)。

于 2013-01-27T10:27:45.467 回答
1

我认为这是因为 JVM 保留了helper=new Helper(). 我的意思是只有在创建对象之后才会发生分配。如果我错了,请纠正我。

于 2013-01-27T09:52:36.657 回答
0

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

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

于 2013-01-27T10:12:51.020 回答
-1

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

synchronized 内部只能相互独占执行,这就是一致性的来源。但同样:if (helper == null)只有在为 Helper 分配了对 Helper 实例的新引用后,helper 不能再变为 null 时,才允许使用外部。

于 2013-01-27T09:59:33.587 回答