设计模式之单例模式

设计模式之单例模式

  • 单例模式的定义

    单例模式(Singleton Pattern) 确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

  • 实现单例模式的步骤:

    • 持有一个私有的自身的引用
    • 构造函数私有化只能自行实例化
    • 提供一个公有的方法提供外部回去该实例化对象
  • 单例模式的优缺点

    • 优点

      • 减少内存开支,由于内存中只有一个实例,所有的访问者都拿到的是同一个实例,不需要频繁的去创建实例减少的内存的开支。
      • 减少了性能的开销,当一个对象比较大的时候,频繁的创建实例会消耗大量的性能,单例模式可以减少性能的开销。
      • 单例模式可以避免对资源的多重占用,比如一个写文件的动作,由于只有一个实例在内存中,避免对同一个资源文件的同时写入操作。
      • 可以为系统提供一个统一的访问点
    • 缺点

      • 单例模式一般没有接口,扩展很困难,不适用于变化的对象
      • 单例类的职责过重,在一定程度上违背了“单一职责原则
      • 单例模式不利于单元测试

    注意:在多线程并发中使用单例模式需要考虑线程安全问题,若不考虑线程安全问题有可能出现多个对象

  • 单例模式的几种写法

    • 恶汉式

      public class Singleton{
              private static Singleton instance = new Singleton();
      
              private  Singleton() {
              }
              public static Singleton getInstance() {
                  return instance;
              }
          }
      

      这种方式是利用classloader的加载机制,在类被加载的时候静态变量初始化的时候创建一个 instance实例。避免了线程安全问题。优点是没有同步代码块,高效;缺点是:类加载的时候就会创建对象,没有实现懒加载

    • 恶汉式(静态内部类)

      public class Singleton{
              private  Singleton() {
              }
              public static Singleton getInstance() {
                  return SingletonHolder.INSTANCE;
              }
      
              private static class SingletonHolder{
                  private static final Singleton INSTANCE = new Singleton();
              }
      
          }
      

      由于静态内部类跟外部类是平级的,所以外部类加载的时候不会影响内部类,因此实现了懒加载,同时也是利用静态变量的方式,使得INSTANCE只会在SingletonHolder加载的时候初始化一次,从而保证不会有多线程初始化的情况,因此也是线程安全的

    • 枚举的方式

       public enum  Singleton{
              INSTANCE;
              Singleton() {
      
              }
              
              public void methead(){
                  
              }
      
          }
      

      采用这种方式实现的单例模式是十分推荐的,天然线程安全,天然解决了序列化和反序列化之后、反射调用私有构造函数出现多个对象的问题。同时这也是Effective Java推荐的写法。

    • 懒汉式(线程不安全)

          public class Singleton {
              private static Singleton instance = null;
      
              private Singleton() {
              }
      
              public static Singleton getInstance() {
                  if (instance == null)
                      return instance;
                  return instance;
              }
          }
      

      这种写法,在多线程的时候,由于并发访问instance,会导致创建多个instance,从而使得单例模式失效。

    • 懒汉式(线程安全1)

       public class Singleton {
              private static Singleton instance = null;
      
              private Singleton() {
              }
      
              public synchronized static Singleton getInstance() {
                  if (instance == null)
                      return instance = new Singleton();
                  return instance;
              }
          }
      

      这种写法的优点是保证了线程安全,缺点是效率低下,因为instance一旦创建,大部分时间都是多线程在访问instance的时候需要进行锁的判断,因此把同步加在方法上会导致多个线程等待

    • 懒汉式(线程安全2)

       public class Singleton {
              private static Singleton instance = null;
      
              private Singleton() {
              }
      
              public static Singleton getInstance() {
                  if (instance == null) {
                      synchronized (Singleton.class) {
                          instance = new Singleton();
                      }
                  }
                  return instance;
              }
          }
      

      这种写法有效的解决了上一种写法多次判断锁的问题,但是这种写法有个致命的缺点就是,当线程A判断instance等于null的时候,这时线程A被挂起,线程B判断instance为null,同时获取到锁进入了同步代码块,然后成功的创建了instance,最后释放了锁退出了同步代码块。恰好此时线程A成功的获取到锁进入同步代码块继续执行,它也会创建一个instance,这样instance就被创建不止一次,系统中就存在多个instance。

    • 懒汉式(双重检测)

      public class Singleton {
              private static Singleton instance = null;
      
              private Singleton() {
              }
      
              public static Singleton getInstance() {
                  if(instance == null) {
                      synchronized(Singleton.class) {
                          if(instance == null) {
                              instance = new Singleton();
                          }
                      }
                  }
                  return instance;
              }
          }
      

      双重校验可以避免上面两种方法的缺点,当线程获得锁后进入同步代码块后,再进一步确认instance是否为null。通常双重校验锁这种形式会比较好的达到正常的单例的效果。

以上就是java开发中常见单例模式的7种写法,我们可以根据不同的环境去选择不用的写法去实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值