单例模式(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);
        }
    }
}

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

相关推荐
小bo波9 小时前
Java Swing 图形用户界面实验 —— 从算术练习到游戏开发的完整实践
java·课程设计·gui·游戏开发·扫雷·swing
咖啡八杯10 小时前
GoF设计模式——备忘录模式
java·后端·spring·设计模式
槑有老呆14 小时前
从 Prompt Engineering 到 Harness Engineering:AI 编程的下一次跃迁
设计模式
SamDeepThinking21 小时前
裁掉那个差程序员后,给你看团队里高手的代码:这个习惯,希望你有
java·后端·程序员
朕瞧着你甚好21 小时前
技术雷达 & Java 集成评估报告 — Apache Tika 3.3.1
java·ai编程
HjhIron1 天前
从Prompt到Context:大模型应用开发的范式转移
设计模式·aigc·ai编程
MacroZheng1 天前
短短几天,暴涨2.8万Star!又一款编程神器开源!
java·人工智能·后端
SamDeepThinking1 天前
函数式编程:用BiFunction消除多类型分支的代码重复
java·后端·面试
Flittly2 天前
【AgentScope Java新手村系列】(16)从RAG到多路检索
java·spring boot·spring
小兔崽子去哪了2 天前
Java 生成二维码解决方案
java·后端