单例模式(三种实现)

2018/03/06 15:01
阅读数 13

1 饿汉式

public class EagerSingleton {
static {
System.out.println("EagerSingleton 被加载");
}

private EagerSingleton(){}  //私有化构造方法,限制直接构造,只能调用 getInstance() 方法获取单例对象


private static final EagerSingleton eagerSingleton=new EagerSingleton(); // 私有化静态 final成员,类加载直接生成单例对象,比较占用内存 
public static EagerSingleton getInstance(){  //提供对外的公共api获取单例对象
return eagerSingleton;
}

}

总结:饿汉式单例的特点:饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成。

 

2 懒汉式

public class LazySingleton {
static {
System.out.println("LazySingleton 被加载");
}

private LazySingleton(){} //私有化构造方法,限制直接构造,只能调用 getInstance() 方法获取单例对象
private static LazySingleton lazySingleton=null;//静态域初始化为null,为的是需要时再创建,避免像饿汉式那样占用内存
public static LazySingleton getInstance(){//提供对外的公共api获取单例对象
if(lazySingleton==null){ 
synchronized (LazySingleton.class){ //在getInstance中做了两次null检查,确保了只有第一次调用单例的时候才会做同步,这样也是线程安全的,同时避免了每次都同步的性能损耗
if(lazySingleton==null){
lazySingleton = new LazySingleton();
}
}
}
return lazySingleton;
}

}

总结:有同步锁的性能消耗

3 静态内部类实现

public class IoDHSingleton {
static {
System.out.println("IoDHSingleton 被加载");
}

private IoDHSingleton(){} //私有化构造方法,限制直接构造,只能调用 getInstance() 方法获取单例对象


public static IoDHSingleton getInstance(){//提供对外的公共api获取单例对象
return HolderClass.ioDHSingleton; //当getInstance方法第一次被调用的时候,它第一次读取HolderClass.ioDHSingleton,内部类HolderClass类得到初始化;而这个类在装载并被初始化的时候,会初始化它的静态域,从而创建ioDHSingleton 的实例,由于是静态的域,                                                            因此只会在虚拟机装载类的时候初始化一次,并由虚拟机来保证它的线程安全性。
}

private static class HolderClass{
static {
System.out.println("HolderClass 被加载");
}
private static IoDHSingleton ioDHSingleton = new IoDHSingleton();
}

 // 防止反序列化获取多个对象的漏洞  

private Object readResolve() throws ObjectStreamException {    

return  HolderClass.ioDHSingleton;  

    }  


}

这个模式的优势在于,getInstance方法并没有被同步,并且只是执行一个域的访问,因此延迟初始化并没有增加任何访问成本。

考虑反射:

 

  由于在调用 SingletonHolder.instance 的时候,才会对单例进行初始化,而且通过反射,是不能从外部类获取内部类的属性的。

  所以这种形式,很好的避免了反射入侵。

 

考虑多线程:

 

  由于静态内部类的特性,只有在其被第一次引用的时候才会被加载,所以可以保证其线程安全性。

 

总结:

  优势:兼顾了懒汉模式的内存优化(使用时才初始化)以及饿汉模式的安全性(不会被反射入侵)。

  劣势:需要两个类去做到这一点,虽然不会创建静态内部类的对象,但是其 Class 对象还是会被创建,而且是属于永久带的对象。

  

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部