💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
# 单例模式 单例模式有非常多种的实现方式,具体如下: ## 饿汉式 - 静态常量 ~~~  package cn.net.smrobot.singleton.type1;  ​  /**   * 饿汉式,使用静态变量的方式   */  public class Singleton {  ​      /**       * 将构造器私有化       */      private Singleton() {}  ​      /**       * 创建本地变量实例       */      private final static Singleton instance = new Singleton();  ​      /**       * 静态方法用来返回实例       * @return       */      public static Singleton getInstance() {          return instance;     }  ​  ​  } ~~~ 优点: 1. 无多线程问题,随着类的加载被加载 缺点: 1. 没有达到懒加载的效果,如果一直没有使用到这个实例,可能会导致内存的浪费。 如果确定该实例一定会被使用,则可以使用这个方式。   ## 饿汉式 - 静态代码块 ~~~  package cn.net.smrobot.singleton.type2;  ​  /**   * 饿汉式 - 使用静态代码块   */  public class Singleton {  ​      public static void main(String[] args) {  ​     }  ​      /**       * 私有化构造方法       */      private Singleton() {}  ​      private static Singleton instance;  ​      /**       * 静态代码块实例对象       */      static {          instance = new Singleton();     }  ​      public static Singleton getInstance() {          return instance;     }  ​  } ~~~ 效果与静态常量的方法是一样的。   ## 懒汉式 - 线程不安全 ~~~  package cn.net.smrobot.singleton.type3;  ​  /**   * 懒汉式   * 线程不安全   */  public class Singleton {      public static void main(String[] args) {          Singleton instance = Singleton.getInstance();          Singleton instance1 = Singleton.getInstance();          System.out.println(instance == instance1);          System.out.println("hashCode = " + instance.hashCode());          System.out.println("hashCode = " + instance1.hashCode());     }  ​      private static Singleton instance;  ​      private Singleton() {}  ​      /**       * 调用该方法的使用才去创建单例对象       */      public static Singleton getInstance() {          if (instance == null) {              instance = new Singleton();         }          return instance;     }  ​  ​  } ~~~ 优点: 1. 可以起到懒加载的效果 缺点: 1. 在多线程的环境下,如果一个线程进入`if(instance == null)`语句判断,但是还没来得及执行`new instance()`,别的线程也进入了这个语句判断,这样可能会导致多个实例对象被创建,达不到单例的效果。 > 在实际开发中不要使用这种方式   ## 懒汉式 - 线程安全 ~~~  package cn.net.smrobot.singleton.type4;  ​  /**   * 懒汉式   * 线程安全 - 使用同步方法   */  public class Singleton {      public static void main(String[] args) {          Singleton instance = Singleton.getInstance();          Singleton instance1 = Singleton.getInstance();          System.out.println(instance == instance1);          System.out.println("hashCode = " + instance.hashCode());          System.out.println("hashCode = " + instance1.hashCode());     }  ​      private static Singleton instance;  ​      private Singleton() {}  ​      /**       * 调用该方法的使用才去创建单例对象       * 加入同步处理代码       * 解决线程安全问题       */      public static synchronized Singleton getInstance() {          if (instance == null) {              instance = new Singleton();         }          return instance;     }  ​  ​  } ~~~ 这种方式使用`synchronized`同步的方式,但是在方法级别进行同步效率比较低,在实际开发中也不推荐使用。   ## 懒汉式 - 同步代码块 ~~~  package cn.net.smrobot.singleton.type5;  ​  /**   * 懒汉式   * 线程安全 - 使用同步代码块   */  public class Singleton {      public static void main(String[] args) {          Singleton instance = Singleton.getInstance();          Singleton instance1 = Singleton.getInstance();          System.out.println(instance == instance1);          System.out.println("hashCode = " + instance.hashCode());          System.out.println("hashCode = " + instance1.hashCode());     }  ​      private static Singleton instance;  ​      private Singleton() {}  ​      /**       * 调用该方法的使用才去创建单例对象       * 加入同步处理代码       * 解决线程安全问题       */      public static Singleton getInstance() {          if (instance == null) {              synchronized (Singleton.class) {                  instance = new Singleton();             }         }          return instance;     }  ​  ​  } ~~~ 仍然是线程不安全的,例如在进行if判断时候有多个线程进入该分支,就会导致对象被同步的创建多次。 **不要使用**   ## 双重检查 - Double Check 推荐使用,可以解决线程安全,懒加载,效率问题。 ~~~  package cn.net.smrobot.singleton.type6;  ​  /**   * 双重检查   */  public class Singleton {      public static void main(String[] args) {          Singleton instance = Singleton.getInstance();          Singleton instance1 = Singleton.getInstance();          System.out.println(instance == instance1);          System.out.println("hashCode = " + instance.hashCode());          System.out.println("hashCode = " + instance1.hashCode());     }  ​      /**       * volatile 关键字很重要       */      private static volatile Singleton instance;  ​      private Singleton() {}  ​      /**       * 调用该方法的使用才去创建单例对象       * 使用两个if进行双重检查       */      public static Singleton getInstance() {          if (instance == null) {              synchronized (Singleton.class) {                  if (instance == null) {                      // 再进行一步判断                      instance = new Singleton();                 }             }         }          return instance;     }  } ~~~   ## 静态内部类 ~~~  package cn.net.smrobot.singleton.type7;  ​  /**   * 静态内部类   */  public class Singleton {      public static void main(String[] args) {  ​     }  ​      private Singleton() {}  ​      /**       * 只有调用getInstance方法时静态内部类才会被加载       * 实例对象会被唯一创建一次       * 利用JVM底层机制类装载的时候是线程安全的       */      private static class SingletonInstance {          private final static Singleton INSTANCE = new Singleton();     }  ​      public static Singleton getInstance() {          return SingletonInstance.INSTANCE;     }  } ~~~ 可以达到线程安全和延迟加载的效果,线程安全是利用JVM的类装载机制,JVM可以保证在加载一个类的时候是线程安全的,推荐使用。   ## 枚举类型 ~~~  package cn.net.smrobot.singleton.type8;  ​  /**   * 枚举模式   */  public enum Singleton {  ​      INSTANCE;        } ~~~ 由于其他单例模式的实质都是将构造方法私有化,但是通过反射是可以将构造方法设置为共有化的,于是就可以创建更多的对象了。而反射的代码会判断该类型是不是枚举类型,如果是枚举类型则会直接抛出异常。同时枚举类型的序列化与反序列化可以保证对象一致。 可以避免多线程同步问题并且能够防止反序列化重新创建新的对象,推荐使用。 > 参考:https://www.cnblogs.com/chiclee/p/9097772.html     ## JDK中的源码 RunTime   ## 单例模式使用场景 1. 需要频繁的进行创建和销毁的对象。 2. 创建对象时耗时过多或者资源浪费过多。 3. 经常用到的对象,工具类对象,频繁访问数据库或文件的对象(数据源,session工厂)。 4. Spring IOC容器的ApplicationContext用的是饿汉式的单例模式。