单例模式:
在学习了狂神和尚硅谷燕姐的视频之后,写一个总结!
-
单例模式:①构造器私有;②自己创建唯一且私有实例;③获取实例的方法公有;
-
解决的问题:一个全局使用的类频繁地创建与销毁。
-
什么时候使用:想控制实例数目,节省系统资源的时候。
-
存在的问题:重点分析懒汉模式,因为饿汉模式是线程安全的,没有花里胡哨的问题;而且有点浪费空间,不管用不用,对象都已经被创建;
使用枚举实现(解决反射)
枚举是抽象类,无法直接实例;只能用实现类里面提供的对象,而且属性用final修饰,在类加载的时候初始化,所以是线程安全的,而且反射都不好使。
因为反射的原理是通过Constructor类的newInstance()方法创建对象的,里面有判断如果是枚举类型,则禁止创建;
饿汉模式:先创建实例,需要用的时候直接用
懒汉模式:需要使用的时候再创建出来
- 饿汉模式的三种实现:
1、普通实现,见代码;
2、枚举实现,减代码
3、静态块实现,见注释,可以再实例化之前读取一些配置文件的参数来创建实例,适用于复杂场景;
public class Singleton01 {
//1、普通 饿汉
//public static final Singleton01 INSTANCE = new Singleton01();
//3、静态代码块 饿汉,可以通过静态块来实现复杂场景,比如,需要通过配置文件读取参数等
public static final Singleton01 INSTANCE;
static{
INSTANCE = new Singleton01();
}
//1、3都可以用的部分
private Singleton01(){//构造私有
}
}
/**
* 饿汉模式
* 2、枚举实现
*/
public enum Singleton02 {//枚举实现饿汉
INSTANCE;
}
懒汉模式的三种方式:
1、普通实现
2、线程安全的DCL实现
3、静态内部类实现
/**
* 问题:当多线程同时判断通过,进入if(INSTANCE == null)
* 就会导致被创建出多个实例对象出来
*/
public class SingleLazy {
private static SingleLazy INSTANCE;
private SingleLazy(){
}
public static SingleLazy getINSTANCE(){
if(INSTANCE == null){
INSTANCE = new SingleLazy();
}
return INSTANCE;
}
}
DCL实现单例:双层检测+synchronized
下面代码的问题:发生指令重排时,按照132顺序执行(请看注释),导致B线程执行到外层if时,认为空间有对象,去直接返回对象,结果对象还没有被实例完全
解决方案:
private static SingleLazy INSTANCE;
替换为**(加volatile)**
private volatile static SingleLazy INSTANCE;
禁止指令重排;
/**
* 问题:INSTANCE = new SingleLazy();创建过程分为三步
* 1、开辟空间
* 2、实例对象
* 3、对象指向内存空间
*/
public class SingleLazy {
private static SingleLazy INSTANCE;
private SingleLazy(){
}
public static SingleLazy getINSTANCE(){
if(INSTANCE == null){//解决效率问题,如果已经有对象了就不用去等锁了
synchronized(SingleLazy.class){//解决多线程问题,确保单例,都卡在if里面的话,可能创建多个对象出来
if(INSTANCE == null){//锁完还是空就放心创建
INSTANCE = new SingleLazy();
}
}
}
return INSTANCE;
}
}
内部类实现
通过内部类来实现,可以保证安全。
因为只有在内部类,被(提供的公开方法)调用的时候才会被类加载器加载,同时创建单例对象,类加载器安全,所以单例安全
public class SingleLazy2 {
private static class Inner{
private static final SingleLazy2 INSTANCE = new SingleLazy2();
}
private SingleLazy2(){
}
public static SingleLazy2 getInstance(){
return Inner.INSTANCE;
}
}
结论:
使用枚举实现(解决反射)
因为反射的原理是通过Constructor类的newInstance()方法创建对象的,里面有判断如果是枚举类型,则禁止创建;
内部类实现
类加载器安全,所以单例安全。
理解枚举,更好的理解单例:
枚举的底层原理