《Java与模式》学习笔记——双重检查成例的研究

Posted brooksychen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《Java与模式》学习笔记——双重检查成例的研究相关的知识,希望对你有一定的参考价值。

成例(Idiom)是一种代码层次上的模式,是在比设计模式的层次更具体的层次上的代码技巧。成例往往与编程语言密切相关。双重检查成例(Double Check Idiom)是从C语言移植过来的一种代码模式。
 
先看一个例子:
 
class  Foo  {
    
private Helper helper = null;
    
public Helper getHelper() {
        
if (helper == null{
            helper 
= new Helper();
        }

        
return helper;
    }

}
 
写出这样的代码,本意显然是要保持在整个JVM中只有一个Helper的实例。但非常明显的是,如果在多线程的环境中运行,上面的代码可能会有两个甚至两个以上的Helper对象被创建出来,从而造成错误。为了克服没有线程安全的缺点,下面给出一个线程安全的例子:
 
class  Foo  {
    
private Helper helper = null;
    
public synchronized Helper getHelper() {
        
if (helper == null{
            helper 
= new Helper();
        }

        
return helper;
    }

}
 
显然,由于整个静态工厂方法都是同步化的,因此,不会有两个线程同时进入这个方法。但是,仔细审查上面的方法会发现,同步化实际上只在helper变量第一次被赋值之前才有用。在helper变量有了值以后,同步化实际上变成了一个不必要的瓶颈。如果能有一个方法去掉这个小小的额外开销,不是更加完美了吗?因此,就有了下面这个设计“巧妙”的双重检查成例。
 
class  Foo  {
    
private Helper helper = null;
    
public Helper getHelper() {
        
if (helper == null{
            
synchronized(this{
                
if (help == null{
                    helper 
= new Helper();
                }

            }

        }

        
return helper;
    }

}
 
可以看到,在上面的方法中,同步化仅用来避免多个线程同时初始化这个类,而不是同时调用这个静态工厂方法。如果这是正确的,那么使用这一个成例之后,“懒汉式”单例类就可以摆脱掉同步化瓶颈,达到一个很妙的境界,如下述代码:
 
public   class  LazySingleton  {
    
private static LazySingleton m_instance = null;
    
private LazySingleton() { }
    
public static LazySingleton getInstance() {
        
if (m_instance == null{
            
synchronized(LazySingleton.class{
                
if (m_instance == null{
                    m_instance 
= new LazySingleton();
                }

            }

        }

        
return m_instance;
    }

}

 
令人吃惊的是,在C语言里得到普遍应用的双重检查成例在多数的Java语言编译器里面并不成立。上面使用了双重检查成例的“懒汉式”单例类,不能工作的基本原因在于,在Java编译器中, LazySingleton类的初始化与m_instance变量赋值的顺序不可预料。如果一个线程在没有同步化的条件下读取m_instance引用,并调用这个对象的方法的话,可能会发现对象的初始化过程尚未完成,从而造成崩溃。
 
一般而言,双重检查成例对Java语言来说是不成立的。在一般情况下,使用饿汉式单例模式或者对整个静态工厂方法同步化的懒汉式单例模式足以解决在实际设计工作中遇到的问题。
 
 
 
后记:其实有很多文章都在讨论Singleton或者Double Checked Idiom,如果您感兴趣,可以读一读。
1、《 解析Java 类和对象的初始化过程》,涉及到Eager Singleton的实例化问题,另外,文中包含关于类的加载、初始化、实例化的一些研究,推荐读一读。
2、《 The "Double-Checked Locking is Broken" Declaration》,一篇详细的讲述Double-Checked Locking的文章,如果你的英文还可以,建议读一读。
3、《 Java中的模式 --单态 (部分翻译 double-checked locking break)》比较详细的讲述了为什么在C++中广泛使用的用Double-Checked Locking来解决Singleton实例化问题的方法在Java中会存在问题,推荐读一读。
4、《 终于可以在Java中使用lazy loading的单态了》,方法非常简单,但多个虚拟机的情况下仍然有问题。另见《 Lazy Singleton的Java实现》。
5、《 How single can your singleton instance be?》,又一篇关于Singleton的讨论,作者是用程序写代码,做法有点BT了,呵呵。
6、 http://www.jdon.com/jivejdon/thread/17578.html,Jdon上关于Single的一些讨论。

以上是关于《Java与模式》学习笔记——双重检查成例的研究的主要内容,如果未能解决你的问题,请参考以下文章

Java并发笔记——单例与双重检测

单例模式双重检查锁定与延迟初始化你不得不知道的底层原理

单例模式双重检查锁定与延迟初始化你不得不知道的底层原理

Java单例模式中双重检查锁的问题

单例模式中的volatile关键字

java单例模式(双重检查加锁)的原因