设计模式之单例设计模式

61 阅读3分钟

一、概述

  • 单例模式:就是采取一定的方法,保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)

二、饿汉式

  • 构造器私有化、类的内部创建对象(在类第一次加载的时候就创建了)、向外暴露一个静态的公共方法
public class Singleton {
  // 静态常量对象
  private final static Singleton instance = new Singleton();
  // 构造器私有化,防止外部可以实例化
  private Singleton() {
  }
  // 获取该类对象引用的唯一方式
  public static Singleton getInstance() {
    return instance;
  }
}
public class Singleton2 {
  // 静态常量对象
  private final static Singleton2 instance;
  // 静态代码块,实例化对象
  static {
    instance = new Singleton2();
  }
  private Singleton2() {
  }
  // 返回对象
  public static Singleton2 getInstance() {
    return instance;
  }
}
  • 优点:写法简单、类加载时就完成了实例化、避免了线程同步问题
  • 缺点:没达到Lazy Loading 的效果,如果从始至终都没有使用过这个实例,就会造成内存浪费

三、懒汉式

1. 不推荐例子

  • 提供一个getInstance()方法,只有在第一次调用的时候,才创建instance
public static Singleton2 getInstance() {
        if (instance == null) {
                instance = new Singleton();
        }
    return instance;
}
  • 优点:Lazy Loading,但只能在单线程时使用
  • 缺点:如果在多线程的情况下,一个线程刚判断完if (instance == null) {},还没来得及实例化对象,另一个线程又进行判断。会导致两个线程同时进入if内部,创建两个实例化对象。

2. 饿汉式(同步方法实现)

public class Singleton3 {
  // 静态对象
  private static Singleton3 instance;
  private Singleton3() {
  }
  // 返回对象,同步方法
  public static synchronized Singleton3 getInstance() {
    // 第一次调用的时候才使用
    if (instance == null) {
      instance = new Singleton3();
    }
    return instance;
  }
}
  • 优点:解决了线程安全问题
  • 缺点:效率低,实际上只需要在第一次创建的时候同步,后面获取对象的时候,不需要同步。这个方法,使得获取对象的时候,需要线程同步。

3. 懒汉式(双重检查)推荐使用

public class Singleton4 {
  // 静态对象,volatile保证修改之后,立刻保存在内存中
  private static volatile Singleton4 instance;  // volatile很重要,可以防止重排序问题

  private Singleton4() {
  }

  // 返回对象
  public static Singleton4 getInstance() {
    // 双重检查
    if (instance == null) {
      synchronized (Singleton4.class) {
        if (instance == null) {
          instance = new Singleton4();
        }
      }
    }
    return instance;
  }
}
  • 通过两次if,保证在非第一次调用时,直接不进if,无需线程同步。
  • 而若两个线程同时进入if的时候,使用同步代码块,只能由一个线程执行对象实例化,而当第二个线程再次进入同步代码块的时候,会发现对象已经被实例化,无需再创建。
  • volatile可以禁止重排序,防止出现空指针异常问题。
  • 优点:延迟加载,效率较高、线程安全

4. 懒汉式(静态内部类)推荐使用

public class Singleton5 {
  private Singleton5() {
  }
        // 静态内部类
  private static class SingletonInstance {
    private final static Singleton5 INSTANCE = new Singleton5();
  }

  // 返回对象
  public static Singleton5 getInstance() {
    return SingletonInstance.INSTANCE;
  }
}
  • 通过采用类装载机制的机制保证实例化实例时只有一个线程
  • 静态内部类在Singleton5类加载时不会被加载,只有在调用getInstance()方法被调用时,才会被加载
  • 优点:避免了线程不安全,利用静态内部类特点实现延迟加载,效率高

四、枚举类实现

public enum Singleton6 {
  INSTANCE;
  public void sayOK() {
    System.out.println("ok");
  }
}
public class Test {
  public static void main(String[] args) {
    Singleton6 instance = Singleton6.INSTANCE;
    Singleton6 instance2 = Singleton6.INSTANCE;
    System.out.println(instance == instance2); // true
    instance.sayOK(); // ok
  }
}
  • 枚举类本身就是一个很好的单例模式,并且可以使用内部的方法。
  • 优点:避免多线程同步问题,而且还能防止反序列化重新创建新的对象