美文网首页
单例模式 之 写法大全

单例模式 之 写法大全

作者: 程就人生 | 来源:发表于2022-02-05 15:35 被阅读0次

    单例模式不仅仅是面试官最爱考核的内容之一,也是程序员们在实际开发中经常用到的设计模式之一,所以它是我们的必备技能之一。说到单例模式,你能想到哪些问题呢?

    1. 单例模式的特点,请说说?

    2. 单例模式,主要有哪些应用场景,你还记得吗?

    3. 单例模式有哪些写法,你还记得多少?

    4. 单例模式是线程安全的吗?

    先上一张思维导图:

    图片

    单例模式:是一种创建型模式,不为别人创建对象,只为自己创建对象,以提供一个全局的唯一的对象实例。这个全局是指在一个容器里的全局。一个启动的项目只保持一个该对象的实例。

    使用场景:生产唯一序列号,Web里的计算器,还要其他比较消耗资源的开销,比如数据库连接池,I/O操作等等。

    缺点:没有接口,不能继承,与单一职责原则冲突。

    背诵再多概念,也不及敲上一段代码。

    1. 单例模式 之 懒汉模式一,线程不安全;

    /**
     * 懒汉式 ,线程不安全
     * 延迟加载
     * @author 程就人生
     * @Date 2022/1/25
     */
    public class Singleton {  
      
      /**
       * 静态的实例变量,实例化后不会被改变
       */
        private static Singleton instance;  
        
        /**
         * 私有的构造函数
         */
        private Singleton (){}  
      
        /**
         * 没加同步锁,不支持多线程,线程不安全
         * @return
         */
        public static Singleton getInstance() {  
          // 延迟加载
          if (instance == null) {  
              instance = new Singleton();  
          }  
          return instance;  
        }  
    }
    
    图片

    懒汉模式有两种,一种是线程不安全的,一种是线程安全的。

    2. 单例模式 之 懒汉模式二,线程安全;

    该实现方式,增加了同步锁,但会对性能有影响。

    /**
     * 懒汉式 ,线程安全
     * 延迟加载
     * @author 程就人生
     * @Date
     */
    public class Singleton {  
      
      /**
       * 静态的实例变量,实例化后不会被改变
       */
        private static Singleton instance; 
        
        /**
         * 私有的构造函数
         */
        private Singleton (){}  
        
        /**
         * 使用了synchronized 关键字,线程安全,支持多线程
         * @return
         */
        public static synchronized Singleton getInstance() {  
          // 延迟加载
          if (instance == null) {  
              instance = new Singleton();  
          }  
          return instance;  
        }  
    }
    

    3. 单例模式 之 饿汉模式一;

    图片
    /**
     * 饿汉式 1
     * 线程安全,非延迟加载
     * 不管是否使用,先进行了实例化,所以称之为饿汉式
     * @author 程就人生
     * @Date 2022/1/25
     */
    public class Singleton {  
    
      /**
       * 先静态,后动态
       * 先属性,后方法
       * 先上后下
       */
        private static Singleton instance = new Singleton();  
        
        /**
         * 私有的构造函数
         */
        private Singleton (){}  
        
        /**
         * 直接返回
         * @return
         */
        public static Singleton getInstance() {  
          return instance;  
        }  
    }
    

    4. 单例模式 之 饿汉模式二;

    /**
     * 饿汉式 静态块单例模式
     * 线程安全,非延迟加载
     * @author 程就人生
     * @Date 2022/1/25
     */
    public class Singleton{
      private static final Singleton instance;
      /**
       * 静态代码块,公共内存区域
       */
      static {
        instance = new Singleton();
      }
      /**
       * 私有的构造函数
       */
      private Singleton (){}  
        
      /**
       * 直接返回
       * @return
       */
      public static Singleton getInstance() {  
         return instance;  
      }    
    }
    

    5. 单例模式 之 双检锁/双重校验锁 (DCL double-checked locking)

    该实现方式,是对懒汉安全模式的升级,提高了性能。

    图片
    /**
     * 双检锁/双重校验锁 (DCL double-checked locking)
     * 线程安全,延迟加载
     * @author 程就人生
     * @Date 2022/1/25
     */
    public class Singleton {  
      
      /**
       * 静态的实例变量,实例化后不会被改变
       * 还使用了 volatile 关键字
       */
        private volatile static Singleton singleton; 
        
        /**
         * 私有的构造函数
         */
        private Singleton (){}  
        
        /**
         * 在获取实例的方法内部使用了同步锁
         * 同步锁是在getInstance方法内,阻塞发生在getInstance方法上,而不是类上;
         * 业务逻辑不复杂,用户感知不到
         * @return
         */
        public static Singleton getInstance() {  
          if (singleton == null) {  
              synchronized (Singleton.class) {  
              if (singleton == null) {  
                // new一个对象做的三件事
                // 1.分配内存给对象
                // 2.初始化对象
                // 3.设置singleton指向刚分配的内存地址
                  singleton = new Singleton();  
              }  
              }  
          }  
          return singleton;  
        }  
    }
    

    6. 单例模式 之 登记式/静态内部类;

    该实现方式,是对 双检锁/双重校验锁 方式的升级;兼顾了饿汉式单例模式的内存浪费和同步锁的性能问题。

    图片
    
    /**
     * 登记式/静态内部类
     * 线程安全,延迟加载
     * 兼顾饿汉单例模式的内存浪费问题和同步的性能问题
     * @author 程就人生
     * @Date 2022/1/25
     */
    public class Singleton {  
        
        /**
         * 私有的构造函数
         * 使用Singleton的时候,默认会先初始化内部类
         */
        private Singleton (){}  
        
        /**
         * 获取实例
         * static 使单例的空间共享,保证这个方法不会被重写、重载
         * @return
         */
        public static final Singleton getInstance() {  
          // 返回结果以前,一定会先调用加载内部类
          return SingletonHolder.INSTANCE;  
        }  
        
        /**
       * 静态内部类,默认不加载
       */
        private static class SingletonHolder {  
          private static final Singleton INSTANCE = new Singleton();  
        }  
    }
    

    7. 单例模式 之 枚举式;

    大师们都推崇的写法,最简易的写法。

    图片
    
    /**
     * 枚举式
     * 线程安全,非延迟加载
     * @author 程就人生
     * @Date 2022/1/25
     */
    public enum Singleton { 
      
        INSTANCE;  
      
      public static Singleton getInstance() {  
          return INSTANCE;  
        }
    }
    

    这些单例模式真的是安全的吗,口说无凭,来一个简单的多线程验证一下吧:

    /**
     * 一个简单的多线程
     * 用多线程测试单例模式的线程安全
     * @author 程就人生
     * @Date
     */
    class Thread1 extends Thread{
      // 线程名称
      private String name;
      
        public Thread1(String name) {
           this.name=name;
        }
      public void run() {
            for (int i = 0; i < 3; i++) {
                System.out.println(name + "运行  :  " + i);
                // 输出单例模式对象的地址
    
                System.out.println(Singleton.getInstance().toString());
                try {
                  //随机休眠
                    sleep((int) Math.random() * 10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }       
      }
    }
    

    在main方法里调用;

    public static void main(String[] argo){    
        Thread1 mTh1=new Thread1("A");
        Thread1 mTh2=new Thread1("B");
        mTh1.start();
        mTh2.start();
    } 
    
    1. 单例模式 之 懒汉模式一,线程不安全,测试结果:
    
    A运行  :  0
    B运行  :  0
    com.example.Singleton@b9098ae
    com.example.Singleton@12b0f150
    B运行  :  1
    com.example.Singleton@b9098ae
    B运行  :  2
    com.example.Singleton@b9098ae
    A运行  :  1
    com.example.Singleton@b9098ae
    A运行  :  2
    com.example.Singleton@b9098ae
    

    第二个地址和其他地址不一样,没有实现实例单一,因此是线程不安全的。

    2. 单例模式 之 懒汉模式二,线程安全,测试结果:

    A运行  :  0
    B运行  :  0
    com.example.Singleton@790d3283
    com.example.Singleton@790d3283
    B运行  :  1
    A运行  :  1
    com.example.Singleton@790d3283
    A运行  :  2
    com.example.Singleton@790d3283
    com.example.Singleton@790d3283
    B运行  :  2
    com.example.Singleton@790d3283
    

    查看输出的实例地址都是一样的,保证了单一实例,因此是线程安全的。

    其他的就不一一验证了,请读者自行验证。

    相关文章

      网友评论

          本文标题:单例模式 之 写法大全

          本文链接:https://www.haomeiwen.com/subject/zgzekrtx.html