Head First Pattern之单例模式

Posted on 2008-09-14 15:40 Lv Yuanfang 阅读(419) 评论(0)  编辑  收藏

多线程环境下的单例模式实现

----Head First Pattern之单例模式



单例模式我想大家都比较熟悉,就是在JVM运行期间一个类只有一个实例,任何时候都是取得同一个实例,也就是一个全局变量了。
单例模式分懒汉式和饿汉式,但是懒汉式的单例在多线程环境下会有同步的问题,下面详细介绍了用3中方法来解决此问题。
单例模式具有以下几个特点:
1.JVM运行期间有且只有一个实例
2.构造函数是私有的
3.通过一个静态工厂方法来获得唯一的实例
4.累内部有一个私有静态实例,通过静态工厂方法创建后,每次再调用静态工厂方法,返回的都是同一个实例

饿汉式:
public class Singleton{
    private static Singleton uniqueInstance = new Singleton();
    // 其他实例变量
    private Singleton(){}
    public static Singleton getInstance(){
        return uniqueInstance;
    }
    
    // 其他方法
}

懒汉式:
public class Singleton{
    private static Singleton uniqueInstance;
    // 其他实例变量
    private Singleton(){}
    public static Singleton getInstance(){
        if(uniqueInstance == null){
            uniqueInstance = new Signleton();
        }
        return uniqueInstance;
    }
    
    // 其他方法
}

多线程环境下的单例模式:
上面的代码就是最基本的单例模式示例代码。但是懒汉式单例有一个问题,因为要保证有且仅有一个实例,如果在多线程环境下调用Singleton.getInstance(),就可能会有多个实例!为了解决多线程访问的问题,有3种解决方法供选择:

1.静态工厂方法加同步关键字,这种方法是在对性能要求不高的情况下采用。
public class Singleton{
    private static Singleton uniqueInstance;
    // 其他实例变量
    private Singleton(){}
    public static synchronised Singleton getInstance(){
        if(uniqueInstance == null){
            uniqueInstance = new Signleton();
        }
        return uniqueInstance;
    }
    
    // 其他方法
}

2.始终用饿汉式单例
public class Singleton{
    private static Singleton uniqueInstance = new Singleton();
    // 其他实例变量
    private Singleton(){}
    public static Singleton getInstance(){
        return uniqueInstance;
    }
    
    // 其他方法
}
饿汉式的方法,会依赖于JVM在加载类的时候,就创建唯一的实例。在每个线程访问getInstance方法前,唯一实例已经被创建。

3.用双检查锁来减少懒汉式中静态方法getInstance的同步开销
对public static synchronised Singleton getInstance()的每次调用,都需要同步,而双检查锁的方式只是在第一次创建实例时同步,其他时候并不需要同步。
public class Singleton{
    private volatile static Singleton uniqueInstance;
    private Singleton(){}
    public static Singleton getInstance(){
        if(uniqueInstance == null){
            synchronised(Singleton.class){
                if(uniqueInstance == null){
                       uniqueInstance = new Singleton();
                }
            }

        }
        return uniqueInstance;
    }
}

如果调用时实例为null,则进入同步区块,此时再进行判断,如果还为null,就创建唯一的实例。有可能在一个线程在 if(uniqueInstance == null) 后进入同步区块前,另一个线程恰好已经创建成功并从同步区块中出来,这就需要进入同步区块后,再做uniqueInstance是否为null的判断。
同时uniqueInstance需要加volatile关键字,保证在创建单例实例时,多个线程能正确处理uniqueInstance变量。

注意:
双检查锁的方式在Java1.4及1.4以前版本不能工作!!因此双检查锁只能在Java 5及以上版本才可以使用。
记得Effictive Java中也提到过双检查锁,也说不能在Java1.4中使用。
原因是Java 1.4及以前的JVM中对volatile关键字的实现允许对双检查锁不合适的同步。(谁能帮我再深入解释下?)原文是:
Unfortunately, in Java version 1.4 and earlier, many JVMs contain implementations of the volatile keyword that allow improper synchronization for double-checked locking. If you must use a JVM other than Java 5, consider other methods of implementing your Singleton.



只有注册用户登录后才能发表评论。


网站导航:
 

posts - 11, comments - 2, trackbacks - 0, articles - 0

Copyright © Lv Yuanfang