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

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

相关推荐
Grey Zeng7 小时前
Java SE 25新增特性
java·jdk·jdk新特性·jdk25
雨白8 小时前
Java 线程通信基础:interrupt、wait 和 notifyAll 详解
android·java
架构师沉默12 小时前
设计多租户 SaaS 系统,如何做到数据隔离 & 资源配额?
java·后端·架构
Java中文社群14 小时前
重要:Java25正式发布(长期支持版)!
java·后端·面试
每天进步一点_JL15 小时前
JVM 类加载:双亲委派机制
java·后端
用户2986985301415 小时前
Java HTML 转 Word 完整指南
java·后端
渣哥15 小时前
原来公平锁和非公平锁差别这么大
java
渣哥16 小时前
99% 的人没搞懂:Semaphore 到底是干啥的?
java
J2K16 小时前
JDK都25了,你还没用过ZGC?那真得补补课了
java·jvm·后端
kfyty72516 小时前
不依赖第三方,不销毁重建,loveqq 框架如何原生实现动态线程池?
java·架构