【问题标题】:What for volatile stuff in Double check? [closed]双重检查中的挥发性物质怎么办? [关闭]
【发布时间】:2011-03-26 19:07:55
【问题描述】:

让我们看看经典的双重检查

class Foo {
    private volatile Foo singleton = null;
    public Foo getFooSingleton() {
        if (singleton == null) {
            synchronized(this) {           
                if (singleton == null)
                    singleton = new Foo();
            }
        }
        return singleton;
    }
}

Volatile modifire 保证“singleton”变量的值将在所有线程中正确显示。但是在当前示例中我真的需要这个吗? 我想不是。所以 - 这就是我认为这个程序以最糟糕的方式运行的方式 - 当一个线程所做的更改不被其他线程看到时。

  1. 线程一进入同步部分并创建单例
  2. 线程二进入同步,同步其当前堆栈值(现在他看到singleton != null),进行第二次检查并退出同步部分。

所以。即使没有 volatile 声明,一切都可以正常工作,甚至更好 =)

【问题讨论】:

  • 我的猜测是,如果 singleton 不再设置为 null,你可能是对的。
  • 我认为您错过了 Java 内存模型不是顺序一致的。也就是说线程可以看到不同顺序的字段读写(如果没有正确同步)。
  • 嗯,你的意思是同步块中没有顺序一致?
  • 不,您不需要 volatile,因为您的“单例”没有任何需要安全发布的状态。由于您的对象没有状态,因此您也不需要单例。由于您的对象没有方法,因此您实际上不需要这里的类。事实上,整个问题毫无意义。

标签: java algorithm concurrency synchronization


【解决方案1】:

是的,这里需要volatile

关键是在创建Foo(包括创建对象和执行其构造函数)和在singleton字段中存储对它的引用之间没有memory barrier,其他线程可以观察到这些动作发生在任意顺序。特别是,在第一次检查期间,线程 2 可以观察到指向部分构造对象的引用。请注意,synchronized 块在这里无济于事,因为线程 2 看到 singleton != null 并且根本没有进入它。

使用volatile 可确保放置适当的内存屏障(从 Java 5 开始;在以前的版本中,根本无法实现双重检查习语)。

【讨论】:

  • 嗯,我记得 se 6 规范说构造函数保证在传递到对象的链接之前完成(除了不寻常的、手工制作的、构造函数的引用转义)
  • 没有。 “就在对新创建对象的引用作为结果返回之前,指示的构造函数被处理”-对于所有对象-查看对规范的引用
  • @user293756:这是单线程语义。多线程语义在java.sun.com/docs/books/jls/third_edition/html/memory.html 中定义,除了final 字段外,它不提供关于构造函数的任何特殊保证。
  • 没有。这里只讨论了最终语义。为在引用对象之前完成的任何构造函数提供保证
【解决方案2】:

Java 中 DCL 的问题不在于额外同步的成本。真正的问题是,如果没有 volatile 修饰符(在 Java 5 之前),您的线程可能会看到构造不正确的 singleton 对象。

只是为了清楚。写入singleton 引用并写入singleton 的字段在单线程程序中是顺序的,在多线程中可能会出现乱序。

【讨论】:

  • 所以?在 java 6 中,没有 volatile 一切都好吗?
  • @user293756 - 没有。实际上,如果您真的想使用 DCL,您必须使用 volatile
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-02
  • 2018-06-20
相关资源
最近更新 更多