饿汉式
饿汉式是最简单最直接的,在单例数量较少的应用里推荐使用这种方式。
优点: 线程安全
缺点: 类加载时就初始化,耗费不必要的资源
直接赋值
public class HungrySingleton {
private static final HungrySingleton INSTANCE = new HungrySingleton();
private HungrySingleton(){}
public static HungrySingleton getHungrySingleton() {
return INSTANCE;
}
}
静态块赋值
public class HungryStaticSingleton {
private static final HungryStaticSingleton INSTANCE;
static {
INSTANCE = new HungryStaticSingleton();
}
private HungryStaticSingleton(){}
public static HungryStaticSingleton getHungrySingleton() {
return INSTANCE;
}
}
懒汉式
简单版
优点: 调用时才加载,对比饿汉式减少资源开销
缺点: 线程不安全,并发情况下可能创建多实例破话单例模式
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton(){}
public static LazySingleton getLazySingleton() {
if (instance == null) {
return new LazySingleton();
}
return instance;
}
}
double check(双重校验)
解决懒汉式线程安全问题,最简单粗暴的方法就是在获取实例方法上加锁。但是这种方式会降低性能,而双重校验单例模式即保证了线程安全又解决了性能问题。
优点: 在简单版基础上解决了线程问题,且性能最优
缺点: 代码逻辑复杂、可读性查,编码不优雅
public class LazyDoubleCheckSingleton {
private static LazyDoubleCheckSingleton instance;
private LazyDoubleCheckSingleton(){}
public static LazyDoubleCheckSingleton getLazySingleton() {
// 并发情况下,之前线程已经创建了对象,之后线程就会被拦截不走同步方法,提升性能
if (instance == null) {
synchronized (LazyDoubleCheckSingleton.class) {
// 并发情况下,初始并发下判断不重复创建
if (instance == null) {
return new LazyDoubleCheckSingleton();
// 这里可能会出现CPU指令重排序的问题 [参考这篇文章解决](https://www.cnblogs.com/xll1025/p/6486170.html)
}
}
}
return instance;
}
}
内部类
优点: 对比double check方式,优雅的解决了线程、性能问题(内部类是延时加载)
public class LazyStaticInnerSingleton {
private LazyStaticInnerSingleton(){
}
public static final LazyStaticInnerSingleton getInstance(){
return LazyHolder.INSTANCE;
}
private static final class LazyHolder{
public static final LazyStaticInnerSingleton INSTANCE = new LazyStaticInnerSingleton();
}
}
懒汉式、饿汉式 都存在一个问题:通过反射机制调用私有构造方法,创建多个实例破坏单例模式。使用注册式(枚举式、容器式)可以解决这个问题,枚举底层使用一个Map容器实现的,和spring使用的容器式比较类似