• 单例模式详解


    单例模式

    基础概念

    单例模式是什么有什么用

    单例模式保证一个类只有一个实例,并提供一个可以访问该实例的方法

    单例模式的几种写法

    1. 饿汉式

      饿汉式是在初始化时就将单例对象创建出来。通常通过属性new创建自身。由JVM保证线程安全,但会造成内存资源的浪费

      //饿汉式
      public class Singleton {
          //私有成员变量
          private static Singleton singleton;
          
          private Singleton(){
              
          }
          public static Singleton getInstance(){
              return singleton;
          }
      }
      
    2. 懒汉式

      懒汉式是指在第一次使用的时候才将对象创建出来,是线程不安全的,但是不会造成内存资源的浪费

      //懒汉式
      public class Singleton {
          
          private static Singleton singleton = null;
      
          private Singleton(){
      
          }
          public static Singleton getInstance(){
              if(singleton == null) {
                  singleton = new Singleton();
              }
              return singleton;
          }
      }
      

      这种方法的不安全性主要在当还没有对象时,有多个线程同时运行,发现没有实例对象,就自己生成了实例对象,这样就有可能出现多个实例对象

    3. 双重校验锁

      //双重校验锁
      public class Singleton {
          //使用volatile关键字保证指令不会被重排序
          private volatile static Singleton singleton = null;
          private Singleton(){
      
          }
          
          public static Singleton getInstance(){
              //如果没有进行实例化
              if(singleton == null) {
                  //将整个类锁住
                  synchronized (Singleton.class) {
                      //确保没有进行实例化
                      if(singleton == null) {
                          singleton = new Singleton();
                      }
                  }
              }
              return singleton;
          }
      }
      
    4. 静态内部类

      //静态内部类
      public class StaticSingleton {
          //私有化构造器
          private StaticSingleton() {
              
          }
          //使用内部类创建对象,由JVM保证线程安全
          private static class SingletonFactory {
              private static StaticSingleton singleton = new StaticSingleton();
          }
          public static StaticSingleton getInstance() {
              return SingletonFactory.singleton;
          }
      }
      
    5. 枚举

      public enum SingletonEnum {
          INSTANCE;
      }
      

    如何破坏单例模式

    1. 反射

      通过反射获取单例对象的构造器,暴力破解后即可创建多个实例

      @Test
      public static void reflectTest() {
      Singleton singleton = Singleton.getInstance();
              Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
              constructor.setAccessible(true);
              Singleton singleton2 = constructor.newInstance(null);
          System.out.println(singleton == singleton2);
      }
      
    2. 序列化

      通过深克隆复制对象,可以生成多个实例

      @Test
      public static void serializationTest() {
              ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("objectFile"));
              Singleton singleton3 = Singleton.getInstance();
              os.writeObject(singleton3);
              os.close();
              ObjectInputStream is = new ObjectInputStream(new FileInputStream(new File("objectFile")));
              Singleton singleton4 = (Singleton) is.readObject();
              is.close();
              System.out.println(singleton3 == singleton4);
      }
      

    如何防止单例模式被破坏

    1. 防止反射破坏单例模式

      新增一个volatile变量isFirstCreate,在私有构造方法里面对该变量进行双校验。

    2. 防止序列化破坏单例模式

      继承Serializable接口,并重写readObject()方法

    import java.io.ObjectStreamException;
    import java.io.Serializable;
    
    // 能防止被反射、序列化破坏的双重校验单例模式
    public class Singleton implements Serializable {
        private volatile static Singleton singleton = null;
        private volatile static boolean isFirstCreate = true;
        private Singleton(){
            //防止被反射破坏单例
            if (isFirstCreate) {
                synchronized (Singleton.class) {
                    if(isFirstCreate) {
                        isFirstCreate = false;
                    }
                    else {
                        throw new RuntimeException("此单例已经创建过了");
                    }
                }
            }
            else {
                throw new RuntimeException("此单例已经存在");
            }
        }
        public static Singleton getInstance(){
            if(singleton == null) {
                synchronized (Singleton.class) {
                    if(singleton == null) {
                        singleton = new Singleton();
                    }
                }
            }
            return singleton;
        }
        //防止被序列化生成多个实例
        private Object readResolve() throws ObjectStreamException {
            return singleton;
        }
    }
    
  • 相关阅读:
    Transact_SQL小手册(各种sql语句大集合)
    矮人DOS工具箱 使用说明
    window.showModalDialog以及window.open用法简介 (转)
    正则表达式(转)
    Ajax.net用户指南(转)
    Java相关的开源GIS系统
    数据库操作之ODBC
    编译第一个OSG程序时候需要注意的
    OSG编译
    VC 多线程编程(转)
  • 原文地址:https://www.cnblogs.com/ZJHqs/p/15079841.html
Copyright © 2020-2023  润新知