5 单例模式

  • 单例模式是保证实例唯一性的重要手段。
  • 单例模式首先通过将类的实例化方法私有化来防止程序通过其他方式创建该类的实例,然后通过提供一个全局唯一获取该类实例的方法帮助用户获取类的实例,用户只需也只能通过调用该方法获取类的实例。
  • 单例模式的常见写法有懒汉模式(线程安全)、饿汉模式、静态内部类、双重校验锁,如下。

5.1 懒汉模式(线程安全)

  • 定义一个私有的静态对象 instance,之所以定义instance为静态,是因为静态属性或方法是属于类的,能够很好地保障单例对象的唯一性;

  • 然后定义一个加锁的静态方法获取该对象,如果该对象为null,则定义一个对象实例并将其赋值给instance,这样下次再获取该对象时便能够直接获取了。

    • 懒汉模式在获取对象实例时做了加锁操作,因此是线程安全的。
 public class LazySingleton {
     // 定义一个私有的静态对象
     private static LazySingleton instance;
 ​
     public LazySingleton() {
     }
 ​
     // 懒汉模式在获取对象实例时做了加锁操作,因此是线程安全的
     public static synchronized LazySingleton getInstance() {
         if (instance == null) {
             instance = new LazySingleton();
         }
         return instance;
     }
 }

5.2 饿汉模式

  • 在类中直接定义全局的静态对象的实例并初始化,然后提供一个方法获取该实例对象。
  • 在饿汉模式下,在 Class Loader 完成后该类的实例便已经存在于 JVM 中了。
 public class HungrySingleton {
     
     // 饿汉模式是在定义单例对象的同时将其实例化的,直接使用便可。
     // 也就是说,在饿汉模式下,在Class Loader完成后该类的实例便已经存在于JVM中了
     private static HungrySingleton instance = new HungrySingleton();
     public HungrySingleton() {
     }
     public static HungrySingleton getInstance() {
         return instance;
     }
 }

5.3 静态内部类

  • 在类中定义一个静态内部类,将对象实例的定义和初始化放在内部类中完成, 我们在获取对象时要通过静态内部类调用其单例对象。
  • 之所以这样设计,是因为类的静态内部类在 JVM 中是唯一的,这很好地保障了单例对象的唯一性。
 // 之所以这样设计,是因为类的静态内部类在JVM中是唯一的,这很好地保障了单例对象的唯一性
 public class Singleton {
     // 在类中定义一个静态内部类,将对象实例的定义和初始化放在内部类中完成
     private static class SingletonHolder {
         private static final Singleton INSTANCE = new Singleton();
     }
     public Singleton() {
     }
 ​
     public static final Singleton getInstance() {
         return SingletonHolder.INSTANCE;
     }
 }

5.4 双重校验锁

  • 在懒汉模式的基础上做进一步优化,给静态对象的定义加上 volatile 锁来保障初始化时对象的唯一性,在获取对象时通过 synchronized (Singleton.class) 给单例类加锁来保障操作的唯一性。
 public class Lock2Singleton {
     private volatile static Lock2Singleton singleton;//对象锁
     public Lock2Singleton() {
     }
     public static Lock2Singleton getInstance() {
         if (singleton == null) {
             synchronized (Lock2Singleton.class){//方法锁
                 if (singleton == null){
                     singleton = new Lock2Singleton();
                 }
             }
         }
         return singleton;
     }
 }