单例模式
一种设计模式,用该模式设计的类,只能创建出一个实例
- 场景:
- Spring: 一个Component就只有一个实例
- JavaWeb: 一个Servlet只有一个实例
- 文件系统: 一个操作系统只能有一个文件系统
实现
用到较多的单例模式实现方式:饿汉式、懒汉式、双重检测锁、静态内部类。
- 实现要点:
- 隐藏构造器
- static Singleton实例
- 暴露实例的获取方法
- 追求的目标
- 线程安全
- 调用效率高
- 延迟加载
1、饿汉式
/**
*
* @description: 饿汉式
* @author: huang xin
* @create: 2018-10-25 20:46
*/
public class HungerSingleton {
private HungerSingleton(){} //构造函数私有化,不能在类的外部创建该类的实体
/*
* 在类加载时候就创建了对象,天然的线程安全
* 问题:没有调用方法创建对象,造成资源浪费
* */
private static final HungerSingleton singleton = new HungerSingleton();
/*
* 静态方法直接返回静态变量,方法没有同步(synchronized),调用效率高
* */
public static HungerSingleton getInstance(){
return singleton;
}
}
2、懒汉式
/**
* @description: 懒汉式
* @author: huang xin
* @create: 2018-10-25 21:04
*/
public class LazySingleton {
/*
* 懒汉式,顾名思义是类加载是不创建对象,而是调用getInstance()时创建对象
* 问题:需要考虑线程同步,效率低
* */
private LazySingleton(){} //构造器隐藏起来
/*
* 类加载时没有初始化,延迟加载
* 不能声明为final,因为getInstance()需要singleton赋值
* */
private static LazySingleton singleton ;
/*
* 在方法上加上线程同步(synchronized)
* 弊端:第二次以后获取单例对象,都要进入锁。耗时
* */
public static synchronized LazySingleton getInstance(){
if (null == singleton){ //第一次创建后,不在创建
singleton = new LazySingleton();
}
return singleton;
}
}
3、双重检测锁
双重检测锁是懒汉式的优化,避免每次实例化都会同步(synchronized)耗时,加入两个if判断。
/**
* @description: 双重检测锁,适用于JDK1.5之后的版本
* @author: huang xin
* @create: 2018-10-25 21:29
*/
public class DoubleCheckSingleton {
private DoubleCheckSingleton(){}
/*
* 需要使用volatile
* 保证所有的写(write)都将先行发生于读(read)
* */
private static volatile DoubleCheckSingleton singleton;
/*
* 只在第一次实例化进入锁
* 优点:可以达到延迟加载的要求,调用效率高
* */
public static DoubleCheckSingleton getInstance(){
if (singleton == null){ //第一次检测;存在实例就不进入
synchronized (DoubleCheckSingleton.class){
if (singleton == null){ //第二次检测:防止B线程在获得A线程释放的锁后重入
singleton = new DoubleCheckSingleton();
}
}
}
return singleton;
}
}
4、静态内部类
/**
* @description: 静态内部类实现
* @author: huang xin
* @create: 2018-10-25 21:48
*/
public class StaticClassSingleton {
/*
* 外部类没有static属性,因此加载本类时不会立即初始化对象
* */
private static final class InnerClassSingleton{
private static final StaticClassSingleton singleton = new StaticClassSingleton();
}
private StaticClassSingleton(){} //构造器隐藏,但内部类可访问的到
/*
* 有真正调用getInstance方法时, 才会加载静态内部类(延迟加载),
* 而且加载类是天然的线程安全的(线程安全), 没有synchronized(调用效率高)
* */
public static StaticClassSingleton getInstance(){
return InnerClassSingleton.singleton; //访问内部类的属性
}
}
实现对比
方式 | 优点 | 缺点 |
---|---|---|
懒汉式 | 线程安全, 可以延迟加载 | 调用效率不高 |
恶汉式 | 线程安全, 调用效率高 | 不能延迟加载 |
双重检测锁 | 线程安全, 调用效率高, 可以延迟加载 | 、 |
静态内部类 | 线程安全, 调用效率高, 可以延迟加载 | 、 |