单例模式
一、饿汉模式
public class SingleTon{
private static INSTANCE = new SingleTon();
private SingleTon(){
}
public static SingleTon getSingleTon(){
return INSTANCE;
}
}
总结:用空间换时间 在一开始就实例化,会稍微浪费点内存,但是优点是不用关心多线程问题
二、懒汉模式
public class SingleTon{
private static INSTANCE = null;
private SingleTon(){}
public static SingleTon getSingleTon(){
if(INSTANCE == null){
INSTANCE = new SingleTon();
}
return INSTANCE;
}
}
总结 用时间换空间,在调用getSingleTon时才会真正创建实例,但是缺点是多线程时,无法保证是真正的单例。
三、双重锁懒汉模式
public class SingleTon{
private static INSTANCE = null;
private SingleTon(){}
public static getSingleTon(){
synchronized(SingleTon.class){
if(INSTANCE == null){
INSTANCE = new SingleTon();
}
}
return INSTANCE;
}
}
总结 DCL模式,只有在对象需要被使用时才创建,加了synchronized后比懒汉模式更安全 多线程情况下也没问题。这样既可以节约内存空间,又可以保证线程安全。但是由于jvm存在乱序执行功能,DCL也会出现线程不安全的情况
INSTANCE = new SingleTon();
这个步骤,其实在jvm里面的执行分为三步:
- 在堆内存开辟内存空间
- 在堆内存中实例化SingleTon里面的各个参数
- 把对象指向堆内存空间 由于jvm乱序功能,所以可能2还没执行时,就先执行了3,如果此时再被切换到线程B上,由于执行了3,INSTANCE已经非空了,会被直接拿出来用,这样就会出现异常。 解决:定义为
private volatile static SingleTon INSTANCE = null;
四、静态内部类模式
public class SingleTon{
private SingleTon(){}
private static class SingleTonHoler{
private static SingleTon INSTANCE = new SingleTon();
}
public function SingleTon getInstance(){
return SingleTonHoler.INSTANCE;
}
}
总结:静态内部类的优点是:外部类加载时并不需要立即加载内部类,内部类不被加载则不去初始化INSTANCE,故不占内存。即当SingleTon第一次被加载时,并不需要去加载SingleTonHoler,只有当getInstance()方法第一次被调用时,才会去初始化INSTANCE,第一次调用getInstance()方法会导致虚拟机加载SingleTonHoler类,这种方法不仅能确保线程安全,也能保证单例的唯一性,同时也延迟了单例的实例化。