Java 实现单例模式

172 阅读7分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

Java 实现单例模式

前置了解

  • 单例模式(Singleton Pattern):保证一个类只有一个实例,并提供一个访问它的全局访问点

  • 单例模式下的类都是只创建一个唯一实例的。

  • 饿汉单例模式:在程序开始运行时,就将该单例对象加载到内存,即预先加载

  • 懒汉单例模式:使用到这个类时才创建实例,即懒加载

破坏单例模式

  • 无论是通过懒汉式还是饿汉式实现的单例模式,都可能通过反射反序列化破坏掉单例的特性,可以创建多个对象

反射破坏单例模式

  • 反射破坏单例模式: 利用反射,可以强制访问单例类的私有构造器,创建新的对象。

  • 原理:通过调用构造方法生成新的对象

  • 阻止方案:在构造方法中添加 if 判空,满足实例不存在才创建实例。

反序列化破坏单例模式

  • 反序列化破坏单例模式: 通过 readObject 方法读取对象时会返回一个新的对象实例。

  • 原理:单例类实现了序列化接口 Serializable, 就可以通过反序列化破坏单例;默认的反序列化方法 readObject 会返回一个新的对象实例

  • 阻止方案 1:不需要序列化时,就不实现序列化接口 Serializable

  • 阻止方案 2:重写反序列化方法 readObject,让其直接返回单例实例对象

总结

  • 注意:在整个单例模式实现中,统一用 getInstance 方法获取该类的单例实例。

  • 构造方法都是私有的(private)。

  • static 表示 共享,final 表示不可变,private 表示私有。

饿汉单例模式

  • 优点:没有加锁,执行效率高;线程安全

  • 缺点:类加载就初始化,getInstance 方法只是将对象输出,浪费内存。

  • 直接创建一个 静态不可变常量 来存储类实例,在声明时直接赋值

懒汉单例模式

  • 共有的优点:实例在调用 getInstance 方法时才会创建实例,这样是不占内存的。

普通懒汉模式

  • 优点:没有加锁,执行效率高。

  • 缺点:线程不安全。

  • 创建一个 静态常量 来存储类实例,初始值为 null

  • if判断是否为 null,是则 创建类实例,否则 无操作。

加了同步锁的懒汉模式

  • 优点:线程安全。

  • 缺点:锁的粒度太大,影响了程序的执行效率,效率低

  • 创建一个 静态常量 来存储类实例,初始值为 null

  • if判断是否为 null,是则 创建类实例,否则 无操作。

  • 在 getInstance 方法上加 synchronized 锁

  • 这样线程就必须排队使用 getInstance 方法

使用类锁的懒汉模式

  • 优点:线程安全;使用 synchronized 代码块优化执行时间,减少锁的粒度。

  • 创建一个 静态常量 来存储类实例,初始值为 null

  • if判断是否为 null,是则 创建类实例,否则 无操作。

  • 第一个 if 判空 用来阻止实例创建完成后,后续的线程进入 创建类实例流程。

  • 第二个 if 判空 用来阻止实例创建完成后,前面在 synchronized 代码块排队的线程进入 创建类实例流程。

  • synchronized 代码块 是方法 synchronized 的优化版,在关键代码位置添加,功能上没有差别,但效率却提高了

使用内部类的懒汉模式

  • 优点:没有加锁,执行效率高;线程安全;不依赖 JDK 版本;外部类加载时,并不会加载内部类实例,而是在调用 getInstance 方法时才会 new 内部类

  • 缺点:是通过虚拟机来保证方法使用锁,来保证线程安全,会增加 JVM 压力。

  • 类实例存储在 类的私有内部类的 静态不可变常量 中,即单例持有者是:内部类

  • 内部类的 静态不可变常量的创建是在第一次调用 getInstance 方法时开始的。

  • 内部类在实现中是没有创建的,因为不需要,如果要创建,那是在第一次调用 getInstance 方法时开始创建。

  • 静态内部类模式创建单例类实例是使用 JVM 机制保证线程安全

使用枚举类的懒汉模式

  • 优点:唯一一种不会被破坏的单例实现模式;线程安全;没有加锁,执行效率高;外部类加载时,并不会加载内部枚举类实例,而是在调用 getInstance 方法时才会 new 内部枚举类

  • 缺点:JDK 1.5 后才能使用。

  • 枚举实例只能装载一次,同样,实例的属性也不会变

  • 类实例存储在 类的私有内部枚举类的 属性 中,即单例持有者是:内部枚举类

  • 内部枚举类的创建是在第一次调用内部枚举类的 getInstance 方法时开始的。

  • 不需要额外的操作即可防止破环单例模式

实现

饿汉单例模式

  • 类代码
class HungrySingletonMode {
   
   private static final HungrySingletonMode HUNGRY_SINGLETON_MODE = new HungrySingletonMode();
   
   private HungrySingletonMode() {
   }
   
   public static HungrySingletonMode getInstance() {
      return HUNGRY_SINGLETON_MODE;
   }
   
   public String getClassIntroduce() {
      return "className:" + HungrySingletonMode.class + ";Introduce:普通的饿汉单例模式";
   }

}
  • 怎么获取实例?
public class Main {
   
   public static void main(String[] args) {
      HungrySingletonMode hungrySingletonMode = HungrySingletonMode.getInstance();
      System.out.println(hungrySingletonMode.getClassIntroduce());
   }

}

懒汉单例模式(普通实现)

  • 类代码
class LazySingletonMode {
   
   private static LazySingletonMode LAZY_SINGLETON_MODE = null;
   
   private LazySingletonMode() {
   }
   
   public static LazySingletonMode getInstance() {
      // 第一次加载时才创建
      if(LAZY_SINGLETON_MODE == null) {
         LAZY_SINGLETON_MODE = new LazySingletonMode();
      }
      return LAZY_SINGLETON_MODE;
   }
   
   public String getClassIntroduce() {
      return "className:" + LazySingletonMode.class + ";Introduce:普通的懒汉单例模式";
   }

}
  • 怎么获取实例?
public class Main {
   
   public static void main(String[] args) {
      LazySingletonMode lazySingletonMode = LazySingletonMode.getInstance();
      System.out.println(lazySingletonMode.getClassIntroduce());
   }

}

懒汉单例模式(同步锁实现)

  • 类代码
class LazySingletonMode1 {
   
   private static LazySingletonMode1 LAZY_SINGLETON_MODE_1 = null;
   
   private LazySingletonMode1() {
   }
   
   public static synchronized LazySingletonMode1 getInstance() {
      // 在 LazySingletonMode 的基础上,加了 synchronized(同步锁)
      if(LAZY_SINGLETON_MODE_1 == null) {
         LAZY_SINGLETON_MODE_1 = new LazySingletonMode1();
      }
      return LAZY_SINGLETON_MODE_1;
   }
   
   public String getClassIntroduce() {
      return "className:" + LazySingletonMode1.class + ";Introduce:加了同步锁的懒汉单例模式";
   }

}
  • 怎么获取实例?
public class Main {
   
   public static void main(String[] args) {
      LazySingletonMode1 lazySingletonMode1 = LazySingletonMode1.getInstance();
      System.out.println(lazySingletonMode1.getClassIntroduce());
   }

}

懒汉单例模式(类锁实现)

  • 类代码
class LazySingletonMode2 {
   
   // 添加 volatile 是为了禁止 指令重排,解决 指令重排 问题。
   // volatile 是 JVM 提供的轻量级同步机制
   // 作用是: 保证可见性;禁止指令重排;但不保证原子性
   private static volatile LazySingletonMode2 LAZY_SINGLETON_MODE_2 = null;
   
   private LazySingletonMode2() {
   }
   
   public static LazySingletonMode2 getInstance() {
      // 在 LazySingletonMode1 的基础上,将 synchronized(同步锁)改为:使用 synchronized 声明的方法
      // 双重检验首先判断实例是否为空,然后使用 synchronized (class) 使用类锁,锁住整个类。
      // 执行完代码块的代码之后,新建了实例后,因为第二重 if 其他代码都不走 if () 里面,只会在最开始的时候效率变慢。
      if(LAZY_SINGLETON_MODE_2 == null) {
         synchronized(LazySingletonMode2.class) {
            if(LAZY_SINGLETON_MODE_2 == null) {
               LAZY_SINGLETON_MODE_2 = new LazySingletonMode2();
            }
         }
      }
      return LAZY_SINGLETON_MODE_2;
   }
   
   public String getClassIntroduce() {
      return "className:" + LazySingletonMode2.class + ";Introduce:使用类锁的懒汉单例模式";
   }

}
  • 怎么获取实例?
public class Main {
   
   public static void main(String[] args) {
      LazySingletonMode2 lazySingletonMode2 = LazySingletonMode2.getInstance();
      System.out.println(lazySingletonMode2.getClassIntroduce());
   }

}

懒汉单例模式(内部类实现)

  • 类代码
class LazySingletonMode3 {
   
   private LazySingletonMode3() {
      System.out.println("className:" + LazySingletonMode3.class + " 创建了!");
   }
   
   public static LazySingletonMode3 getInstance() {
      return SingletonHolder.LAZY_SINGLETON_MODE_3;
   }
   
   public String getClassIntroduce() {
      return "className:" + LazySingletonMode3.class + ";Introduce:使用内部类的懒汉单例模式";
   }
   
   /**
    * 内部类
    */
   public static class SingletonHolder {
      
      private static final LazySingletonMode3 LAZY_SINGLETON_MODE_3 = new LazySingletonMode3();
      
      public SingletonHolder() {
         System.out.println("className:" + SingletonHolder.class + " 创建了!");
      }
   }

}
  • 怎么获取实例?
public class Main {
   
   public static void main(String[] args) {
      LazySingletonMode3 lazySingletonMode3 = LazySingletonMode3.getInstance();
      System.out.println(lazySingletonMode3.getClassIntroduce());
   }

}

懒汉单例模式(枚举实现)

  • 类代码
class LazySingletonMode4 {
   
   private LazySingletonMode4() {
      System.out.println("className:" + LazySingletonMode4.class + " 创建了!");
   }
   
   public static LazySingletonMode4 getInstance() {
      return SingletonHolder.INSTANCE.getInstance();
   }
   
   public String getClassIntroduce() {
      return "className:" + LazySingletonMode4.class + ";Introduce:使用内部类的懒汉单例模式";
   }
   
   /**
    * 枚举
    * <p>
    * 枚举类型是线程安全的,并且只会装载一次
    */
   private enum SingletonHolder {
      
      /**
       * 表示实例,每一个枚举只会装载一次
       */
      INSTANCE;
      
      private final LazySingletonMode4 instance;
      
      SingletonHolder() {
         this.instance = new LazySingletonMode4();
         System.out.println("className:" + SingletonHolder.class + " 创建了!");
      }
      
      private LazySingletonMode4 getInstance() {
         return instance;
      }
   }

}

  • 怎么获取实例?
public class Main {
   
   public static void main(String[] args) {
      LazySingletonMode4 lazySingletonMode4 = LazySingletonMode4.getInstance();
      System.out.println(lazySingletonMode4.getClassIntroduce());
   }

}

引用与参考