Posted on 2008-09-14 15:40
Lv Yuanfang 阅读(430)
评论(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.