Singleton单例设计模式详解

目录

模式定义

保证一个类只有一个实例,并且提供一个全局访问点

应用场景

重量级的对象,不需要多个实例,如线程池,数据库连接池

实现方式

1.懒汉模式:

延迟加载 ,只有在真正使用的时候,才开始实例化。

1)线程安全问题。

2)double check 加锁优化。

3)编译器(JIT),CPU 有可能对指令进行重排序,导致使用到尚未初始化的实例,可以通过添加volatile 关键字进行修饰,对于volatile 修饰的字段,可以防止指令重排。

java 复制代码
public class LazySingletonTest {
    public static void main(String[] args) {
//        LazySingleton instance1 = LazySingleton.getInstance();
//        LazySingleton instance2 = LazySingleton.getInstance();
//        System.out.println(instance1==instance2); //true
        new Thread(() -> {
            LazySingleton instance1 = LazySingleton.getInstance();
            System.out.println(instance1);
        }).start();

        new Thread(() -> {
            LazySingleton instance2 = LazySingleton.getInstance();
            System.out.println(instance2);
        }).start();

    }
}

class LazySingleton {
    private volatile static LazySingleton instance;
    private LazySingleton() {
    }
    public static LazySingleton getInstance() {
        if (instance == null) {
            synchronized (LazySingleton.class){
                if (instance == null) {
                    instance = new LazySingleton();
                    // 字节码层
                    // JIT , CPU 有可能对如下指令进行重排序
                    // 1. 分配空间 2. 初始化 3. 引用赋值
                }
            }
        }
        return instance;
    }
}

2.饿汉模式:

类加载的初始化阶段就完成了实例的初始化 。

本质上就是借助于jvm类加载机制 ,保证实例的唯一性(初始化过程只会执行一次)及线程安全(JVM以同步的形式来完成类加载的整个过程)。

类加载过程:

  1. 加载二进制数据到内存中, 生成对应的Class数据结构,
  2. 连接: a. 验证, b.准备(给类的静态成员变量赋默认值),c.解析
  3. 初始化: 给类的静态变量赋初值
    只有在真正使用对应的类时,才会触发初始化 如( 当前类是启动类即main函数所在类,直接进行new 操作,访问静态属性、访问静态方法,用反射访问类,初始化一个类的子类等.)
java 复制代码
public class HungrySingletonTest {
    public static void main(String[] args) {
        HungrySingleton instance = HungrySingleton.getInstance();
        HungrySingleton instance1 = HungrySingleton.getInstance();
        System.out.println(instance==instance1);
    }
}
class HungrySingleton {
    private static HungrySingleton instance=new HungrySingleton();
    private HungrySingleton(){
    }
    public static HungrySingleton getInstance(){
        return instance;
    }
}

3.静态内部类

1).本质上是利用类的加载机制 来保证线程安全

2).只有在实际使用的时候,才会触发类的初始化,所以也是懒加载的一种形式。

java 复制代码
public class InnerClassSingletonTest {
    public static void main(String[] args) {
//        InnerClassSingleton instance = InnerClassSingleton.getInstance();
//        InnerClassSingleton instance1 = InnerClassSingleton.getInstance();
//        System.out.println(instance==instance1); //true

        new Thread(() -> {
            InnerClassSingleton instance = InnerClassSingleton.getInstance();
            System.out.println(instance);
        }).start();

        new Thread(() -> {
            InnerClassSingleton instance1 = InnerClassSingleton.getInstance();
            System.out.println(instance1);
        }).start();
    }
}
class InnerClassSingleton{
    private static class InnerClassHolder{
        private static InnerClassSingleton instance = new InnerClassSingleton();
    }
    private InnerClassSingleton(){
    }
    public static InnerClassSingleton getInstance(){
        return InnerClassHolder.instance;
    }
}

反射

java 复制代码
public class InnerClassSingletonTest {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//        InnerClassSingleton instance = InnerClassSingleton.getInstance();
//        InnerClassSingleton instance1 = InnerClassSingleton.getInstance();
//        System.out.println(instance==instance1); //true

//        new Thread(() -> {
//            InnerClassSingleton instance = InnerClassSingleton.getInstance();
//            System.out.println(instance);
//        }).start();
//
//        new Thread(() -> {
//            InnerClassSingleton instance1 = InnerClassSingleton.getInstance();
//            System.out.println(instance1);
//        }).start();

        Constructor<InnerClassSingleton> declaredConstructor = InnerClassSingleton.class.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        InnerClassSingleton innerClassSingleton = declaredConstructor.newInstance();
        InnerClassSingleton instance = InnerClassSingleton.getInstance();
        System.out.println(innerClassSingleton==instance); //false
    }
}
class InnerClassSingleton{
    private static class InnerClassHolder{
        private static InnerClassSingleton instance = new InnerClassSingleton();
    }
    private InnerClassSingleton(){
    }
    public static InnerClassSingleton getInstance(){
        return InnerClassHolder.instance;
    }
}

如何防止反射攻击破坏?

饿汉模式下无法避免,懒汉模式可在构造函数中增加如下代码,判断实例是否存在。

java 复制代码
    private InnerClassSingleton(){
        if (InnerClassHolder.instance!=null){
            throw new RuntimeException("单例不允许多个实例");
        }
    }
java 复制代码
public class InnerClassSingletonTest {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//        InnerClassSingleton instance = InnerClassSingleton.getInstance();
//        InnerClassSingleton instance1 = InnerClassSingleton.getInstance();
//        System.out.println(instance==instance1); //true

//        new Thread(() -> {
//            InnerClassSingleton instance = InnerClassSingleton.getInstance();
//            System.out.println(instance);
//        }).start();
//
//        new Thread(() -> {
//            InnerClassSingleton instance1 = InnerClassSingleton.getInstance();
//            System.out.println(instance1);
//        }).start();

        Constructor<InnerClassSingleton> declaredConstructor = InnerClassSingleton.class.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        InnerClassSingleton innerClassSingleton = declaredConstructor.newInstance();
        InnerClassSingleton instance = InnerClassSingleton.getInstance();
        System.out.println(innerClassSingleton==instance); //false
    }
}
class InnerClassSingleton{
    private static class InnerClassHolder{
        private static InnerClassSingleton instance = new InnerClassSingleton();
    }
    private InnerClassSingleton(){
        if (InnerClassHolder.instance!=null){
            throw new RuntimeException("单例不允许多个实例");
        }
    }
    public static InnerClassSingleton getInstance(){
        return InnerClassHolder.instance;
    }
}

枚举类型

1)天然不支持反射创建对应的实例 ,且有自己的反序列化机制

2)利用类加载机制保证线程安全

java 复制代码
public enum EnumSingletonTest {
    INSTANCE;

    private void print(){
        System.out.println(this.hashCode());
    }
}

class EnumTest{
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException, ClassNotFoundException {
        EnumSingletonTest instance = EnumSingletonTest.INSTANCE;
//        EnumSingletonTest instance1 = EnumSingletonTest.INSTANCE;
//        System.out.println(instance==instance1); // true

//        Constructor<EnumSingletonTest> declaredConstructor = EnumSingletonTest.class.getDeclaredConstructor(String.class,int.class);
//        declaredConstructor.setAccessible(true);
//        EnumSingletonTest instance = declaredConstructor.newInstance("INSTANCE",0);

//        ObjectOutputStream oss = new ObjectOutputStream(new FileOutputStream("enumsingleton"));
//        oss.writeObject(instance);
//        oss.close();

        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("enumsingleton"));
        EnumSingletonTest object = ((EnumSingletonTest) ois.readObject());
        System.out.println(instance==object); //true:有自己的反序列化机制
    }
}

序列化

可以利用指定方法readResolve来替换从反序列化流中的数据 如下

java 复制代码
ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;
ANY‐ACCESS‐MODIFIER Object readResolve() throws ObjectStreamException;
ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;
java 复制代码
public class InnerClassSingletonTest {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException, ClassNotFoundException {
//        InnerClassSingleton instance = InnerClassSingleton.getInstance();
//        InnerClassSingleton instance1 = InnerClassSingleton.getInstance();
//        System.out.println(instance==instance1); //true

//        new Thread(() -> {
//            InnerClassSingleton instance = InnerClassSingleton.getInstance();
//            System.out.println(instance);
//        }).start();
//
//        new Thread(() -> {
//            InnerClassSingleton instance1 = InnerClassSingleton.getInstance();
//            System.out.println(instance1);
//        }).start();

//        Constructor<InnerClassSingleton> declaredConstructor = InnerClassSingleton.class.getDeclaredConstructor();
//        declaredConstructor.setAccessible(true);
//        InnerClassSingleton innerClassSingleton = declaredConstructor.newInstance();
//        InnerClassSingleton instance = InnerClassSingleton.getInstance();
//        System.out.println(innerClassSingleton==instance); //false

        InnerClassSingleton instance = InnerClassSingleton.getInstance();
//        ObjectOutputStream oss = new ObjectOutputStream(new FileOutputStream("testSerializable"));
//        oss.writeObject(instance);
//        oss.close();

        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("testSerializable"));
        InnerClassSingleton object = ((InnerClassSingleton) ois.readObject());
        System.out.println(instance==object);

    }
}
class InnerClassSingleton implements Serializable {

    static final long serialVersionUID =42L;

    private static class InnerClassHolder{
        private static InnerClassSingleton instance = new InnerClassSingleton();
    }
    private InnerClassSingleton(){
        if (InnerClassHolder.instance!=null){
            throw new RuntimeException("单例不允许多个实例");
        }
    }
    public static InnerClassSingleton getInstance(){
        return InnerClassHolder.instance;
    }

    Object readResolve() throws ObjectStreamException{
        return InnerClassHolder.instance;
    }
}

部分源码中的应用定位

感兴趣的可以去官网下载源码深入研究

Spring & JDK

java 复制代码
java.lang.Runtime
java 复制代码
org.springframework.aop.framework.ProxyFactoryBean
java 复制代码
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry
java 复制代码
org.springframework.core.ReactiveAdapterRegistry

Tomcat

java 复制代码
org.apache.catalina.webresources.TomcatURLStreamHandlerFactory

反序列化指定数据源

java 复制代码
java.util.Currency

创作不易,点个关注吧!!!
创作不易,点个关注吧!!!
创作不易,点个关注吧!!!

相关推荐
大飞pkz2 小时前
【设计模式】备忘录模式
开发语言·设计模式·c#·备忘录模式
charlie1145141913 小时前
精读C++20设计模式——结构型设计模式:享元模式
c++·笔记·学习·设计模式·享元模式·c++20
charlie1145141916 小时前
精读C++20设计模式:结构型设计模式:装饰器模式
笔记·学习·设计模式·程序设计·c++20·装饰器模式
charlie1145141916 小时前
精读C++20设计模式——行为型设计模式:解释器模式
c++·学习·设计模式·解释器模式·c++20
StevenGerrad18 小时前
【读书笔记】架构整洁之道 P2~3 编程范式&设计原则
设计模式·架构·软件工程
大飞pkz1 天前
【设计模式】迭代器模式
开发语言·设计模式·c#·迭代器模式
稚辉君.MCA_P8_Java1 天前
DeepSeek Java 单例模式详解
java·spring boot·微服务·单例模式·kubernetes
zero13_小葵司1 天前
在不同开发语言与场景下设计模式的使用
java·开发语言·javascript·设计模式·策略模式
坐不住的爱码1 天前
单例模式入门
单例模式
CoderIsArt1 天前
四种对象型创建模式:抽象工厂、 build模式、原型ProtoType与单例模式
单例模式·原型模式