单例模式(Singleton Pattern)

单例模式(Singleton Pattern)

定义

是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。

隐藏起所有的构造方法。

属于创建型模式。


适用场景

确保任何情况下都绝对只有一个实例。

比如数据库连接池、配置文件读取、缓存等。


标准示例

1. 饿汉式单例

在单例类首次加载时,就创建实例。

  • 优点:执行效率高,性能高,没有锁
  • 缺点:某些情况下,可能会有内存浪费(系统中单例类很多的时候,无论是否使用,加载时都会初始化到内存)
java 复制代码
public class HungrySingleton {
    //静态成员变量
    private static final HungrySingleton INSTANCE;
    //代码块对成员变量赋初始值
    static {
        INSTANCE = new HungrySingleton();
    }

    //私有的构造函数
    private HungrySingleton(){}

    //全局访问方法
    public static HungrySingleton getInstance(){
        return INSTANCE;
    }
}

2. 懒汉式单例

被外部类调用时才会创建实例。

  • 优点:节省了内存
  • 缺点:线程不安全问题
java 复制代码
public class LazySingleton {
    private static LazySingleton instance;

    private LazySingleton(){}

    public static LazySingleton getInstance(){
        if(instance == null){
            // 位置A
            instance = new LazySingleton();
            // 位置B
        }
        // 位置C
        return instance;
    }
}

上述代码的线程不安全体现在两种情况:

两个线程输出实例相同,实际上是后者覆盖了前者:

有两个线程,都执行到了位置B,这时后者会覆盖掉前者,当线程继续到位置C时,它们输出的确是同一个实例 ------把前者覆盖掉的那个实例。

两个线程同时进入非空判断条件中,然后按顺序返回,输出两个不同实例:

有两个线程,一个执行到了位置A,另一个执行到了位置B,然后位置B的线程执行到位置C,输出它的实例;位置A的也随后执行到位置B,创建了新的实例覆盖原有实例,然后执行到位置C输出该新实例。两次输出的是不同的实例

解决懒汉式单例线程安全问题的方法一:加锁

java 复制代码
public class LazySingleton {
    private static LazySingleton instance;
    
    private LazySingleton(){
    }

	//加锁解决线程安全问题
    public synchronized static LazySingleton getInstance(){
        if(instance == null){
            instance = new LazySingleton();
        }
        return instance;
    }
}

但是,这种方式会有副作用,所有的获取实例的调用,都会受到synchronized的性能影响。

解决懒汉式单例线程安全问题的方法二:双重检查

java 复制代码
public class DoubleCheckLazySingleton {
	//加 volatile 是为了解决指令重排序的问题,保证instanc变量先创建
    private volatile static DoubleCheckLazySingleton instance;

    private DoubleCheckLazySingleton(){
    }

    public static DoubleCheckLazySingleton getInstance(){
        //第一次检查,目的是要判断是否要加锁
        if(instance == null){
            synchronized (DoubleCheckLazySingleton.class){
                //第二次检查,目的是判断是否要创建实例
                if(instance == null) {
                    instance = new DoubleCheckLazySingleton();
                }
            }
        }
        return instance;
    }
}

上述代码可以解决问题,只是可读性不够高。

解决懒汉式单例线程安全问题的方法三:静态内部类

利用原理:内部类只有在调用时才会被加载。

java 复制代码
public class StaticInnerClassLazySingleton {
    private StaticInnerClassLazySingleton(){}

    public static StaticInnerClassLazySingleton getInstance(){
        return LazyInnerClass.INSTANCE;
    }

    private static class LazyInnerClass{
        private static final StaticInnerClassLazySingleton INSTANCE = new StaticInnerClassLazySingleton();
    }
}

3. 注册式单例

将每一个实例都缓存到统一的容器中,使用唯一标识获取实例

java 复制代码
public class ContainerSingleton {
    private ContainerSingleton(){}

    private static ContainerSingleton instance;

    private static Map<String,Object> ioc = new ConcurrentHashMap<String, Object>();

    public static Object getInstance(String className){
        Object instance = null;
        if(!ioc.containsKey(className)){
            try {
                instance = Class.forName(className).newInstance();
                ioc.put(className,instance);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return  instance;
        }else{
            return ioc.get(className);
        }
    }
}

以上就是 单例模式 的全部内容,感谢阅读。

相关推荐
linmoo19863 分钟前
java脚手架系列16-AI大模型集成
java·人工智能·ai·大模型·通义千问·qwen·脚手架
对酒当歌丶人生几何3 分钟前
Mybatis控制台打印SQL执行信息(执行方法、执行SQL、执行时间)
java·数据库·sql·mybatis
uncleqiao11 分钟前
5.Feign与ReflectiveFeign
java·feign
yours_Gabriel16 分钟前
【微服务】 Eureka和Ribbon
java·微服务·eureka·ribbon
岳轩子26 分钟前
23种设计模式之原型模式
设计模式·原型模式
小扳28 分钟前
微服务篇-深入了解使用 RestTemplate 远程调用、Nacos 注册中心基本原理与使用、OpenFeign 的基本使用
java·运维·分布式·后端·spring·微服务·架构
阿乾之铭29 分钟前
Spring Framework的常用工具类
java
北巷!!33 分钟前
宇信科技JAVA笔试(2024-11-26日 全部AK)
java·开发语言·科技
ᝰꫝꪉꪯꫀ36134 分钟前
JavaWeb——SpringBoot原理
java·开发语言·后端·springboot
HaiFan.39 分钟前
Spring日志
java·spring boot