单例设计模式(转)

原文地址:http://droidyue.com/blog/2015/01/11/looking-into-singleton/

 

一,概念

单例模式:指的是一个类只有一个实例,并且提供一个全局访问点。

二,实现思路

1,基本思路:

        1)在单例的类中设置一个private静态变量Instance,Instance类型为当前类,用来持有单例唯一的实例

        2)将(无参数)构造器设置为private的,避免外部使用new构造多个实例

        3)提供一个public的静态方法,如getInstance,用来返回该类的唯一实例Instance

(上面提到的思路的单例实例可以有以下几种创建形式,每一种实现都需要保证实例的唯一性!)

2,饿汉式:

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

        2)饿汉式的常见代码如下,当SingleInstance类加载时会执行

private static SingleInstance Instance = new SingleInstance(); 

初始化了唯一的实例,然后getInstance()直接返回Instance即可。

public class SingleInstance {
  private static SingleInstance Instance = new SingleInstance();
  
  private SingleInstance() {
  }
  
  public static SingleInstance getInstance() {
      return Instance;
  }
}

        3)饿汉式的问题——如果构造方法中存在过多的处理,会导致加载这个类时比较慢,可能引起性能问题+如果使用饿汉式的话,只进行了类的装载,并没有实质的调用,会造成资源的浪费。

3,懒汉式:

        1)懒汉式指的是单例实例在第一次使用时进行创建。这种情况下避免了上面饿汉式可能遇到的问题。但是考虑到多线程的并发操作,我们不能简简单单地像下面代码实现。

public class SingleInstance {
  private static SingleInstance Instance;
  private SingleInstance() {
  }
  
  public static SingleInstance getInstance() {
      if (null == Instance) {
          Instance = new SingleInstance();
      }
      return Instance;
  }
}

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

4,synchronized修饰方法:

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

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

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

5,双重检查加锁:

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

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

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

6,利用static机制:

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

public class SingleInstance {
  private SingleInstance() {
  }
  
  public static SingleInstance getInstance() {
      return SingleInstanceHolder.Instance;
  }
  
  private static class SingleInstanceHolder {
      private static SingleInstance Instance = new SingleInstance();
  }
}

猜你喜欢

转载自coyotestark.iteye.com/blog/2292658
今日推荐