Wetts's blog

Stay Hungry, Stay Foolish.

0%

设计模式-具体-单例模式.md

单例模式(Java)

  • 懒汉
    • 第一次调用 getInstance() 方法时初始化对象
    • 会有线程安全问题。两个线程同时获取对象,但是都还未初始化完成,则会返回两个不同的对象。虽然可以对方法用 synchronized,但是每次获取都需要加锁,降低效率。
  • 双重检查锁定
    • 需要用 volatile 修饰对象,以免对象初始化的时候指令重排。如下所示,如果 2 和 3 之间重排,则另一个线程可能会获得一个还未初始化的对象。
      1. 分配对象的内存空间
      2. 初始化对象
      3. 设置 uniqueInstance 指向刚分配的内存地址
  • 饿汉
    • 在类加载的时候直接创建这个对象,这样既能提高效率,又能保证线程安全。
  • 静态内部类
    • 饿汉式的方式只要 Singleton 类被装载了,那么 uniqueInstance 就会被实例化(没有达到 lazy loading 效果),而这种方式是 Singleton 类被装载了,uniqueInstance 不一定被初始化。因为 SingletonHolder 类没有被主动使用,只有显示通过调用 getInstance 方法时,才会显示装载 SingletonHolder 类,从而实例化 uniqueInstance
  • 枚举
    • 用枚举实现单例模式可以避免如下 2 个问题,其他四种方式都不能避免
      1. 序列化造成单例模式不安全
      2. 反射造成单例模式不安全
  • 如何防止反射、克隆、序列化对单例模式的破环
    • 防止反射破环(虽然构造方法已私有化,但通过反射机制使用 newInstance() 方法构造方法也是可以被调用):
      • 首先定义一个全局变量开关 isFristCreate 默认为开启状态
      • 当第一次加载时将其状态更改为关闭状态
    • 防止克隆破环
      • 重写 clone(),直接返回单例对象
    • 防止序列化破环
      • 添加 readResolve(),返回 Object 对象