【设计模式二】——单例模式

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

单例的特点:

  • 某个类只能有一个实例;(构造器私有)
  • 它必须自行创建这个实例;(自己编写实例化逻辑)
  • 它必须自行向整个系统提供这个实例;(对外提供实例化方法)

类图如下:
在这里插入图片描述
单例模式分为懒汉式的和饿汉式,有的地方也会讲登记式的单例模式,学习一下这三种单例模式。

饿汉式

public class Person {
  // 直接创建一个本类的对象
  private static final Person instance = new Person();

  // 构造器私有,外部不能实例化
  private Person() {
  }

  public static Person getInstance() {
    return instance;
  }
}

懒汉式

public class Person {
  private static Person instance;

  // 构造器私有,外部不能实例化
  private Person() {
  }
  public static Person getInstance() {
    // 如果没有实例再去创建
    if (instance == null) {
      Person person = new Person();
      instance = person;
    }
    return instance;
  }
}

多线程下使用懒汉式,会破坏单例模式。

方法加锁

public class Person {
  private static Person instance;

  // 构造器私有,外部不能实例化
  private Person() {
  }

  // 单例提供给外部的获取方法
  // 1. public static synchronized Person getInstance() 锁太大导致效率低下
  public static synchronized Person getInstance() {
    // 如果没有实例再去创建
    if (instance == null) {
      Person person = new Person();
      instance = person;
    }
    return instance;
  }
}

直接给获取实例的方法加上synchronized可以结局多线程下的问题,但是此方法会导致效率低下。

双重检查锁 + volatile

 private volatile static Person instance;
  // 构造器私有,外部不能实例化
  private Person() {
  }

  // 单例提供给外部的获取方法
  // 1. 双重检查锁 + 内存可见性
  public static synchronized Person getInstance() {
    // 如果没有实例再去创建
    if (instance == null) {
      synchronized (Person.class) {
        if (instance == null) {
          Person person = new Person();
          instance = person;
        }
      }
    }
    return instance;
  }

使用双重检查锁可以提高效率。使用volatile可以导致指令重排。

防止序列化破坏单例模式

一个单例对象创建好后,有时候需要将对象序列化然后写入磁盘,下次使用时再从磁盘中读取对象并进行反序列化,将其转化为内存对象。反序列化后的对象会重新分配内存,即重新创建,如果序列化的对象目标为单例对象,就违背了单例模式的初衷,相当于破坏了单例。

解决办法:在类中重写readResolve()方法即可
在这里插入图片描述

防止反射破坏单例模式

饿汉式

如果是饿汉式单例模式,直接在构造函数中判断实例是否为null,代码如下:

public class Person {
  private volatile static Person instance = new Person();

  // 构造器私有,外部不能实例化
  private Person() {
    if (instance != null) {
      throw new RuntimeException("单例不允许多实例");
    }
  }

  public static Person getInstance() {
    return instance;
  }

懒汉式

增加一个标志位防止反射破坏

public class Person {
  private volatile static Person instance;
  private static boolean flag = false;

  // 构造器私有,外部不能实例化
  private Person() {
    synchronized (Person.class) {
      if (!flag) {
        flag = true;
      } else {
        throw new RuntimeException("单例不允许多实例");
      }
    }
  }


  public static synchronized Person getInstance() {
    // 如果没有实例再去创建
    if (instance == null) {
      synchronized (Person.class) {
        if (instance == null) {
          Person person = new Person();
          instance = person;
        }
      }
    }
    return instance;
  }
}

其实标志位也不能防止反射破坏单例模式,可以通过枚举类实现单例模式防止反射破化。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值