设计模式之单例模式
文章转载至:http://www.blogjava.net/kenzhh/archive/2013/03/15/357824.html
当一个类只需要创建一个实例即可时,则需要使用单例模式。
第一种(懒汉,线程不安全):
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if(null!=instance){
instance = new Singleton();
}
return instance;
}
}
这使用的是一种懒加载方式,即当需要使用时才去判断选择创建;若此时同时有多个调用就会出现多次创建的问题;因此线程不安全
第二种(懒汉,线程安全):
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if(null!=instance){
instance = new Singleton();
}
return instance;
}
}
这种通过加synchronized锁的方式虽然线程安全,但是效率非常的低。
第三种(饿汉):
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
这种方式基于classloder机制避免了多线程的同步问题,不过,instance在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用getInstance方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化instance显然没有达到的懒加载效果。
第四种(饿汉,变种):
public class Singleton {
private static Singleton instance = null;
static {
instance = new Singleton();
}
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
这个和第三种差不多。
第五种(静态内部类):
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
这种方式同样利用了classloder的机制来保证初始化instance时只有一个线程,它跟第三种和第四种方式不同的是(很细微的差别):第三种和第四种方式是只要Singleton类被装载了,那么instance就会被实例化(没有达到lazy loading效果),而这种方式是Singleton类被装载了,instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance。想象一下,如果实例化instance很消耗资源,我想让他延迟加载,另外一方面,我不希望在Singleton类加载时就实例化,因为我不能确保Singleton类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化instance显然是不合适的。这个时候,这种方式相比第三和第四种方式就显得很合理。
第六种(枚举):
public class Singleton {
private Singleton(){}
public static Singleton getInstance(){
return SingletonEnum.INSTANCE.getInstance();
}
private static enum SingletonEnum{
INSTANCE;
private Singleton singleton;
//JVM会保证此方法绝对只调用一次
private SingletonEnum(){
singleton = new Singleton();
}
public Singleton getInstance(){
return singleton;
}
}
}
这种方式是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。但是我目前在实际开发中还没看到过这种写法。
第七种(双重校验锁):
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (null == singleton) {
synchronized (Singleton.class) {
if (null == singleton) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
这个是第二种方式的升级版,俗称双重检查锁定。在JDK1.5之后,双重检查锁定才能够正常达到单例效果。
不过好像在高并发的情况下,可能会出现空对象的问题;即在new对象时,Java虚拟机完全有可能先new出来一个空的未调用过构造函数的instance对象,然后再将其赋值给instance引用,然后再调用构造函数,对instance对象当中的元素进行初始化。当还未初始化时正好有个线程在调用,此时会直接返回,而返回的对象还未被初始化。但是对于普通的一般不会出现此种问题。
不过一般来说,第一种不算单例,第四种和第三种就是一种,如果算的话,第五种也可以分开写了。所以说,一般单例都是五种写法。懒汉,恶汉,双重校验锁,枚举和静态内部类。