单例模式
核心作用
- 保证一个类只有一个实例,并且提供一个访问该实例的全局访问点
常见场景
- Windows的回收站、任务管理器
- 项目中,读取配置文件的类,一般也只有一个对象,没有必要每次都去new对象读取
- 数据库连接池的设计也是单例模式
- 在Spring中,每个bean默认就是单例模式
优点:
- 单例模式只有一个实例,减少了系统的开销
- 可以在系统设置全局的访问点,优化共享资源访问
五种常见的实现单例模式的方式
- 饿汉式(线程安全,调用效率高,不能延迟加载)
- 懒汉式(线程安全,调用效率不高,可以延迟加载)
- DCL懒汉式(由于JVM底层内部模型的原因,偶尔会出现问题,不建议使用)
- 饿汉式改进 (静态内部类式:线程安全,调用效率高,可以延时加载)
- 枚举单例(线程安全,调用效率高,不能延时加载,推荐使用)
饿汉式
public class SingletonHunger {
//1、私有化构造器
private SingletonHunger(){}
//2、类初始化的时候,立即加载该对象
private static SingletonHunger instance=new SingletonHunger();
//3、提供获取该对象的方法,没有synchronized,效率高
public static SingletonHunger getInstance(){
return instance;
}
}
懒汉式
public class SingletonLazyer {
//1、私有化构造器
private SingletonLazyer(){}
//2、类初始化的时候,不立即加载对象
private static SingletonLazyer instance;
//3、提供获取该对象的方法,有synchronized,效率低一些
public static synchronized SingletonLazyer getInstance(){
if (instance==null){
instance=new SingletonLazyer();
}
return instance;
}
}
DCL懒汉式(双重检测)
因为不是原子性操作,在极端情况下:
- 1分配内存
- 2执行构造方法
- 3指向地址
- 加volatile,保证线程对此变量修改时,另一个线程,该变量的缓存就失效,直接读取内存中的值,总之,一个线程如果改变了某个变量的值,在另外一个线程中是立即生效的
public SingletonDCLLazyer{
//1、私有化构造器
private SingletonDCLLazyer(){};
//2、类初始的时候,不立即加载对象
private volatile static SingtonDCLLazyer instance;
//3、提供获取对象的方法,有synchrond,效率低一些
private static SingletonDCLLazyer getInstance(){
if(instance==null){
synchronized(SingletonDCLLayer.class){
if(instance=null){
//检测为空,说明线城是第一个竞争到SingletonDCLLazyer这个锁的
instance=new SingletonDCLLazyer();
}
}
}
return instance
}
}
饿汉改进式
public class SingletonHungerUpgrade {
//1、私有化构造器
private SingletonHungerUpgrade(){}
//2、类初始化的时候,不立即加载对象
private static class InnerClass{
private static final SingletonHungerUpgrade instance =
new SingletonHungerUpgrade();
}
static SingletonHungerUpgrade getInstance(){
return InnerClass.instance;
}//只有在调用getInstance的是时候才会加载内部类 -0
}
枚举单例
//枚举单例模式(不能延时加载,线程安全,最推荐,底层源码即屏蔽了反射对线程安全的破坏)
public enum SingletonEnum {
//创建对象;
INSTANCE;
public SingletonEnum getInstance(){
return INSTANCE;
}
}