单例模式解析

其中单例(Singleton)模式应该是我们耳熟能详的一种模式。本文将比较特别的介绍java设计模式中的单例模式。

概念

单例模式,又称单件模式或者单子模式,指的是一个类只有一个实例,并且提供一个全局访问函数。

实现思路

  • 在单例的类中设置一个private静态变量sInstance,sInstance类型为当前类,用来持有单例唯一的实例。
  • 将(无参数)构造器设置为private,避免外部使用new构造多个实例。
    其中上面的单例实例有一下几种实现方式,每种实现方式都需要保证实例的唯一性
饿汉式

饿汉式指的是单例的实例在类装载时进行创建。如果单例类的构造方法中没有包含过多的操作处理,饿汉式其实是可以接受的。

饿汉式的常见代码如下,当SingleInstance类加载时会执行private static SingleInstance sInstance = new SingleInstance();初始化了唯一的实例,然后getInstance()直接返回sInstance。

public class SingleInstance {
  private static SingleInstance sInstance = new SingleInstance();

  private SingleInstance() {
  }

  public static SingleInstance getInstance() {
      return sInstance;
  }
}
饿汉式的问题
  • 因为是类加载时初始化,如果构造方法中存在过多的处理,会导致加载这个类时比较慢,可能引起性能问题。
  • 如果只进行了类的装载,并没有实质的调用,会造成资源的浪费。
懒汉式

懒汉式指的是单例实例在第一次使用时进行创建。这种情况下避免了上面饿汉式可能遇到的问题。

但是考虑到多线程的并发操作,我们不能简简单单得像下面代码实现。

public class SingleInstance {
  private static SingleInstance sInstance;
  private SingleInstance() {
  }

  public static SingleInstance getInstance() {
      if (null == sInstance) {
          sInstance = new SingleInstance();
      }
      return sInstance;
  }
}

上述的代码在多个线程密集调用getInstance时,存在创建多个实例的可能。比如线程A进入null == sInstance这段代码块,而在A线程未创建完成实例时,如果线程B也进入了该代码块,必然会造成两个实例的产生。

synchronized修饰符

使用synchrnozed修饰getInstance方法可能是最简单的一个保证多线程保证单例唯一性的方法。
synchronized修饰的方法后,当某个线程进入调用这个方法,该线程只有当其他线程离开当前方法后才会进入该方法。所以可以保证getInstance在任何时候只有一个线程进入。

public class SingleInstance {
  private static SingleInstance sInstance;
  private SingleInstance() {
  }

  public static synchronized SingleInstance getInstance() {
      if (null == sInstance) {
          sInstance = new SingleInstance();
      }
      return sInstance;
  }
}

但是使用synchronized修饰getInstance方法后必然会导致性能下降,而且getInstance是一个被频繁调用的方法。虽然这种方法能解决问题,但是不推荐。

双重检查加锁

使用双重检查加锁,首先进入该方法时进行null == sInstance检查,如果第一次检查通过,即没有实例创建,则进入synchronized控制的同步块,并再次检查实例是否创建,如果仍未创建,则创建该实例。

双重检查加锁保证了多线程下只创建一个实例,并且加锁代码块只在实例创建的之前进行同步。如果实例已经创建后,进入该方法,则不会执行到同步块的代码。

public class SingleInstance {
  private static volatile SingleInstance sInstance;
  private SingleInstance() {
  }

  public static SingleInstance getInstance() {
      if (null == sInstance) {
          synchronized (SingleInstance.class) {
              if (null == sInstance) {
                  sInstance = new SingleInstance();
              }
          }
      }
      return sInstance;
  }
}
volatile

Volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的“可见性”。可见性的意思是当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值。使用volatile修饰sInstance变量之后,可以确保多个线程之间正确处理sInstance变量。
关于volatile,可以访问深入分析Volatile的实现原理了解更多。

利用static机制

在Java中,类的静态初始化会在类被加载时触发,我们利用这个原理,可以实现利用这一特性,结合内部类,可以实现如下的代码,进行懒汉式创建实例。

public class SingleInstance {
  private SingleInstance() {
  }

  public static SingleInstance getInstance() {
      return SingleInstanceHolder.sInstance;
  }

  private static class SingleInstanceHolder {
      private static SingleInstance sInstance = new SingleInstance();
  }
}

关于这种机制,可以具体了解双重检查锁定与延迟初始化

单例 vs static变量

全局静态变量也可以实现单例的效果,但是使用全局变量无法保证只创建一个实例,而且使用全局变量的形式,需要团队的约束,执行起来可能会出现问题。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值