关于volatile variable



  • Chris老师在课上讲到singleton那个例子,最后为了防止object的partial creation,用了volatile variable。但是volatile是non-blocking的,为什么能防止partial creation的情况呢?



  • @snooperlock 有代码吗?好好奇呢





  • @snooperlock 欢迎卷入Java的黑历史……

    在Java 1.4以前,大家用“Double-Checked Locking”的方法来构建Singleton,人们看着是好的,直到有一天咱们的Java之神Joshua Bloch发出惊天檄文The “Double-Checked Locking is Broken” Declaration,来讨伐这种写法的错误。(这时候大家是不用volatile的)。下面例子摘自该檄文,主要阐释“Double-Checked Locking”的问题,并不是Singleton,这点要注意。

    // Broken multithreaded version
    // "Double-Checked Locking" idiom
    class Foo {
        private Helper helper = null;
        public Helper getHelper() {
            if (helper == null) {
                synchronized(this) {
                    if (helper == null) {
                        helper = new Helper();
                    }
    	    }
            }
            return helper;
        }
        // other functions and members...
    }
    

    最重要的问题是在JVM中,给helper【初始化】和【给fields赋值】是out of order的。简单来说,前者分配内存空间并把helper的fields初始化为默认值,比如int的默认值为0;后者会根据constructor内部的逻辑来给fields赋上你需要的值,比如我们可以把某个int设为99。

    在上面代码中,如果我们在【初始化】和【给fields赋值】之间跑了synchronized里面的if (helper == null),我们可能会得到一个非null的helper但是其所有fields都为默认值。这就是所谓的partial creation。


    怎么办呢?幸好各位大神在Java 5.0里面引入volatile解决了这个问题。volatile在Java的memory model里面有个伟大的特性,叫做happen-before relationship

    Any write before the read to the volatile variable has to really happen before the read.

    上面不是废话,放到我们例子中就是,如果helper被declare为volatile,在读取值之前,Java保证所有逻辑上在read之前的write都已经完成,就是说【给fields赋值】一定在read之前完成了。这完美的解决了前面的问题,“Double-Checked Locking” 被fixed了。


    《Effective Java》推荐用Enum来写Singleton,不过了解“Double-Checked Locking”是非常重要的Java基本功:)王婆卖瓜一篇博文

    public enum Helper {
        INSTANCE;
    
        public void someMethod() {...}
    }
    


  • @snooperlock 对了,你贴的代码里面是不是少了一个右括号?左括号比右括号多一个。

    return instance 应该落在第一个if(instance == null)的block外面,而不是里面。



  • @Wenzhe 多谢大神,讲的十分透彻。

    那个"return instance;" 确实应该在 if(instance == null) 的外面



  • @snooperlock 碰巧以前研究过,现在复习一下:8ball: 这是一个很好的问题呢:)


登录后回复
 

与 BitTiger Community 的连接断开,我们正在尝试重连,请耐心等待