Singleton

定义

  • 保证一个类仅由一个实例,并提供全局访问点。

隶属于创建型设计模式

分类

  • 懒汉模式:延迟加载,只有在使用时才开始初始化
    • 线程安全问题
    • double check 加锁优化
    • 编译器JIT、CPU有可能对指令进行重排,导致使用到尚未初始化的实例,可以通过关键字volatile进行修饰,防止指令重排序
  • 饿汉模式: 初始化阶段就完成了实例的初始化,本质上是借助JVM类加载机制,保证实例的唯一性。
    • 类加载过程:类的生命周期分为(装载、连接、初始化、使用、卸载),在连接阶段(验证、准备、解析三个部分)中的准备阶段主要是为static修饰的成员变量分配内存,并设置初始值。final修饰的会直接赋值(而不是使用默认值0/null)。除了final修饰的成员变量会在第三阶段将静态变量赋值,执行顺序时父类静态域和静态代码块,然后就是子类静态域或者子类静态代码块,然后是静态属性。
    • 静态内部类实现

懒汉双检查示例代码

  1. public class LazySingletonTest {
  2. public static void main(String[] args) {
  3. new Thread(() -> {
  4. LazySingleton instance = LazySingleton.getInstance();
  5. System.out.println(instance);
  6. }).start();
  7. new Thread(() -> {
  8. LazySingleton instance = LazySingleton.getInstance();
  9. System.out.println(instance);
  10. }).start();
  11. }
  12. }
  13. class LazySingleton {
  14. private static volatile LazySingleton instance; // volatile 禁止重排序
  15. private LazySingleton() {// 私有构造器
  16. }
  17. public static LazySingleton getInstance() {
  18. if (instance == null) {
  19. synchronized (LazySingleton.class) {
  20. if (instance == null) {
  21. // 字节码层面
  22. // 1.在堆中创建一块地址,并返回地址的引用
  23. // 2.invokespecial 对堆中地址进行初始化
  24. // 3.赋值
  25. instance = new LazySingleton();
  26. }
  27. }
  28. }
  29. return instance;
  30. }
  31. }

饿汉模式实例代码

  1. public class HungrySingletonTest {
  2. public static void main(String[] args) {
  3. HungrySingleton instance = HungrySingleton.getInstance();
  4. HungrySingleton instance1 = HungrySingleton.getInstance();
  5. System.out.println(instance == instance1);
  6. }
  7. }
  8. class HungrySingleton {
  9. // 类加载机制
  10. private static HungrySingleton instance = new HungrySingleton();
  11. private HungrySingleton() {
  12. }
  13. public static HungrySingleton getInstance() {
  14. return instance;
  15. }
  16. }

使用场景

  • 想确保任何情况下绝对只有一个实例(数据库连接池)
  • 可以把工厂类设计成单例模式
  • 单例模式与享元模式
    优点:
  • 在内存中只有一个实例,减少了内存开销。
  • 可以避免对资源的多重占用
  • 设置全局访问点,严格控制访问
    缺点:
  • 没有接口扩展困难

重点

  • 私有构造器
  • 线程安全
  • 延迟加载
  • 序列化和反序列化安全
  • 反射
  • 枚举单例
  1. 单例 Double Check
  2. 静态内部类 基于类初始化的延迟加载解决方案