• 20.单例模式进阶


    1.单例模式能解决啥问题?

    首先,我们先来复习一下什么是单例模式:
    单例模式是一种创建型设计模式, 让你能够保证一个类只有一个实例, 并提供一个访问该实例的全局节点。

    那么问题来了? 什么样的类只能创建一个实例呢?
    比如,在一个系统中,总有一些东西是全局共享且很少改变的。 比如数据库的url , password, username配置信息。我们可以把它放在一个单例类中,然后系统中所有的地方需要用到这些配置的时候,就可以直接通过全局访问点获得此单例类的实例,然后通过调用相关的方法获取相关的配置。

    1.1复习单例模式

    在这里插入图片描述
    单例模式的UML非常简单,首先是私有化构造函数,然后是增加一个对外的对象全局访问点。

    1.1.1 单例类的常见实现方式

    1.1.1.1 懒汉式 :第一次使用时才创建对象

    普通实现,采用方法同步锁,线程安全但是使用时性能比较差

    public class Singleton {  
        //采用static修饰保证此类只有一个实例
        private static Singleton instance;  
        private Singleton (){}
        //用synchronized保证此类不会实例化此类两次  
        public static synchronized Singleton getInstance() {  
            if (instance == null) {  
                instance = new Singleton();  
            }  
            return instance;  
        }  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    采用双重检查锁机制实现安全的懒汉式单例类,线程安全,性能相比前者较高

    public class Singleton {
        //volatile保证此类变量的可见性,保持线程安全      
        private volatile static Singleton singleton;  
        private Singleton (){}  
        public static Singleton getSingleton() {  
        if (singleton == null) {  
            synchronized (Singleton.class) {  
                if (singleton == null) {  
                    singleton = new Singleton();  
                }  
            }  
        }  
        return singleton;  
        }  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    1.1.1.2饿汉式: 类加载的时候就初始化单例对象

    普通实现

    public class Singleton {  
        //final保证了此变量的不可变性,防止反射攻击
        private static final Singleton instance = new Singleton();  
        private Singleton (){}  
        public static Singleton getInstance() {  
        return instance;  
        }  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    静态代码块的方式实现单例类

    /**
     * 静态代码块方式实现单例类,此种方式和和饿汉式效果一样,都是在类加载的时候进行实例化
     */
    public class StaticBlockSingleton {
        //final保证变量的不可变性,防止反射攻击
        private static final StaticBlockSingleton instance;
    
        private StaticBlockSingleton(){}
    
        //静态代码块也是在类加载是进行初始化的,且只执行一次
        static{
            try{
                instance = new StaticBlockSingleton();
            }catch(Exception e){
                throw new RuntimeException("Exception occured in creating singleton instance");
            }
        }
    
        public static StaticBlockSingleton getInstance(){
            return instance;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    1.1.1.3静态内部类实现方式:这种方式即达到了懒加载的效果,也实现了线程安全。

    此种方式相比懒汉式的安全实现比较简单,主要是final和静态内部类的机制发挥作用

    public class Singleton {
        //静态内部类只有在第一次使用时加载  
        private static class SingletonHolder {  
        private static final Singleton INSTANCE = new Singleton();  
        }  
        private Singleton (){}  
        public static final Singleton getInstance() {  
            return SingletonHolder.INSTANCE;  
        }  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    1.1.1.4枚举实现方式: 这是单例模式的最佳实现方式,它简洁,自动支持序列化机制,绝对防止多次实例化。

    枚举实现是利用java内部机制的单例最佳实现,推荐使用。

    /**
     * 枚举单例实现
     * 其实我们采用普通私有化构造器方法是实现单例类的方式在反射面前并不可靠
     * 因为一些客户端使用反射机制能够实例化单例类的第二个实例甚至第n个实例,
     * 如果我们想要抵御这样的攻击,我们可以人为的增加限制,让构造器进行自我检查,就是在构造器尝试实例化第二个实例的时候抛出异常
     * 单例枚举单例可以不用那么麻烦就能做到抵御前面的攻击,枚举对象天然就是单例
     */
    public enum EnumSingleton {
    
        INSTANCE;
        private String name;
        private String code;
    
        public  String getName(){
            return name;
        }
    
        public  String getCode(){
            return code;
        }
    
        public void setName(String name){
            this.name = name;
        }
    
        public void setCode(String code){
            this.code = code;
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    1.1.1.5序列化单例类

    import java.io.Serializable;
    
    /**
     * 可序列化的单例类,用于分布式系统的单例类
     */
    public class SerializedSingleton implements Serializable {
    
        private static final long serialVersionUID = -78834253243544235L;
    
        private SerializedSingleton(){}
    
        private static class SingletonHelper{
            private static final SerializedSingleton instance  = new SerializedSingleton();
        }
    
        public static SerializedSingleton getInstance(){
            return SingletonHelper.instance;
        }
    
        /**
        *通过添加这个方法能防止序列化所造成得到的实例不一致的现象
        */
        protected Object readResolve() {
            return getInstance();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    代码地址:
    https://gitee.com/yan-jiadou/design-mode/tree/master/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/src/main/java/SingletonPattern/first

  • 相关阅读:
    爱尔兰公司改名要遵循一定的程序和规定
    Debezium报错处理系列之六十八:No resolvable bootstrap urls given in bootstrap.servers
    KeyedProcessFunction 在 Flink项目中的应用实战
    【1302. 层数最深叶子节点的和】
    将python项目部署在一台服务器上
    centos7为例进行数据盘挂载详解
    猴子也能学会的jQuery第六期——jQuery事件(中)
    vue官方文档(18) :具名插槽示例
    IPhone无法usb线共享网络给windows电脑的常规解决办法
    手摸手,带你实现移动端H5瀑布流布局
  • 原文地址:https://blog.csdn.net/c1776167012/article/details/126209866