单例模式5种实现方式

       在github上看到一个项目,实现了多种设计模式,就把它fork下来,一个一个看,而后也能够学习参考别人写的代码。
地址:https://github.com/iluwatar/java-design-patterns
(如下代码都转自上面的项目)
       单例模式,也叫单子模式,是一种经常使用的软件设计模式。在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只须要拥有一个的全局对象,这样有利于咱们协调系统总体的行为。好比在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,而后服务进程中的其余对象再经过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。

       实现单例模式的思路是:一个类能返回对象一个引用(永远是同一个)和一个得到该实例的方法(必须是静态方法,一般使用getInstance这个名称);当咱们调用这个方法时,若是类持有的引用不为空就返回这个引用,若是类保持的引用为空就建立该类的实例并将实例的引用赋予该类保持的引用;同时咱们还将该类的构造函数定义为私有方法,这样其余处的代码就没法经过调用该类的构造函数来实例化该类的对象,只有经过该类提供的静态方法来获得该类的惟一实例。
       
单例模式在多线程的应用场合下必须当心使用。若是当惟一实例还没有建立时,有两个线程同时调用建立方法,那么它们同时没有检测到惟一实例的存在,从而同时各自建立了一个实例,这样就有两个实例被构造出来,从而违反了单例模式中实例惟一的原则。 解决这个问题的办法是为指示类是否已经实例化的变量提供一个互斥锁(虽然这样会下降效率)。
参考:wikipediajava

常见的是git

1.静态内部类(也叫饿汉模式)
2.懒汉模式
3.双重锁保证线程安全程序员

还有另外3种写法:github

4.序列化实现
5.枚举实现 web

1.静态内部类 设计模式

//也叫饿汉模式,线程安全
public final class IvoryTower {

  /** * 静态变量属于类,不属于任何独立的对象,因此无需建立类的实例就能够访问静态变量。之因此会产生这样的结果,是由于编译器只为整个类建立了一个静态变量的副本,也就是只分配一个内存空间,虽然有多个实例,但这些实例共享该内存 */
  private static final IvoryTower INSTANCE = new IvoryTower();

  /** * 私有化构造器,使得不能在外部调用构造器,也就不能在外部使用 IvoryTower it =new IvoryTower();的方式建立对象,保证了实例化对象只有一个 */
  private IvoryTower() {}

  /** * 用来返回实例化对象 * * @return instance of the singleton. */
  public static IvoryTower getInstance() {
    return INSTANCE;
  }
}

2.懒汉模式
       和上面的不一样之处在于没有在类加载的时候就建立对象,而是在真正要使用到这个对象的时候再去获取,优势在于若是这个建立对象过程很费时,那么一开始就建立会浪费较多时间,好比几分钟或者几个小时,万一建立好以后还用不到,那不是坑爹~~ 还有就是好比,个人类的构造函数中,有一些事可能须要依赖于别的类干的一些事(好比某个配置文件,或是某个被其它类建立的资源),咱们但愿他能在我第一次getInstance()时才被真正的建立。这样,咱们能够控制真正的类建立的时刻,而不是把类的建立委托给了类装载器。因而有了懒加载。安全

public final class ThreadSafeLazyLoadedIvoryTower {

  private static ThreadSafeLazyLoadedIvoryTower instance;

  private ThreadSafeLazyLoadedIvoryTower() {}

  /** * */
  public  static synchronized ThreadSafeLazyLoadedIvoryTower getInstance() {

    if (instance == null) {   //(1)
                                //(2)
      instance = new ThreadSafeLazyLoadedIvoryTower();//(3) 
    }

    return instance;
  }
}

       注意getInstance()方法处的synchronized关键字,若是没有这个关键字,这个类就是线程不安全的。
懒汉模式在多线程环境下是不安全的,为何这么说呢?假设如今有线程A,线程B,而后在线程A执行到代码(1)处以后,在代码(3)以前因为线程调度使得A暂停运行,一直停留在(2)处。这时候,若是线程B也执行到了代码(1)处的判断,因为线程A还还没有new对象就被暂停了,使得如今的instance仍是null,因此B进行判断以后也会获得instance=null,而后也进入了代码(2),(3)处,顺利的new了一个对象。这时候若是线程A恢复运行,又会new一个对象,就致使了不止一个实例化对象。 可是若是在方法前加了一个synchronized关键字,在多线程环境下,它会使这个方法同时只被一个线程调用,那样就不存在线程A执行到一半被暂停转而被线程B执行,致使最后实例化了2个对象。
       可是思考一下,其实只须要在第一次实例化对象的时候才须要锁,在以后的调用的时候是不须要的,而上面这种写法使得每次调用都是加锁。优化方法就是双重锁,什么意思呢?就是对方法不加synchronized,可是在第一次判断instance==null,若是为null,再加锁,这样就使得加锁过程只会执行一次,一旦被执行过一次,instance!=null以后,就不会再次执行这个锁,就能够提升点效率!服务器

public final class ThreadSafeDoubleCheckLocking {

  private static volatile ThreadSafeDoubleCheckLocking instance;

  /** * private constructor to prevent client from instantiating. */
  private ThreadSafeDoubleCheckLocking() {
    // to prevent instantiating by Reflection call
    if (instance != null) {
      throw new IllegalStateException("Already initialized.");
    }
  }

  /** * Public accessor. * * @return an instance of the class. */
  public static ThreadSafeDoubleCheckLocking getInstance() {
    // local variable increases performance by 25 percent
    // Joshua Bloch "Effective Java, Second Edition", p. 283-284
    ThreadSafeDoubleCheckLocking result = instance;
    if (result == null) {
      synchronized (ThreadSafeDoubleCheckLocking.class) {
        result = instance;
        if (result == null) {
          instance = result = new ThreadSafeDoubleCheckLocking();
        }
      }
    }
    return result;
  }
}

还有2种是序列化的方式和枚举方式
枚举方式:多线程

public enum EnumIvoryTower {
  INSTANCE;
  @Override
  public String toString() {
    return getDeclaringClass().getCanonicalName() + "@" + hashCode();
  }
}

       重写toString()只是为了测试的时候方便看到结果。枚举的方式确实简单明了。默认枚举实例的建立是线程安全的,因此不须要担忧线程安全的问题。可是在枚举中的其余任何方法的线程安全由程序员本身负责。还有防止上面的经过反射机制调用私用构造器。
序列化的方式: socket

public final class InitializingOnDemandHolderIdiom implements Serializable {

  private static final long serialVersionUID = 1L;

  private InitializingOnDemandHolderIdiom() {}

  public static InitializingOnDemandHolderIdiom getInstance() {
    return HelperHolder.INSTANCE;
  }

  protected Object readResolve() {
    return getInstance();
  }

  private static class HelperHolder {
    public static final InitializingOnDemandHolderIdiom INSTANCE =
        new InitializingOnDemandHolderIdiom();
  }

}

       序列化是Java一个强大的功能,它能够将类的信息变成字节的形式存储在硬盘上或者经过socket传输。若是咱们将一个单例类序列化,传输,再进行反序列化,那么如何能保证咱们屡次反序列化以后只会获得同一个实例化对象呢?        readResolve()方法就是用来保证这一点的!并且这份实例代码里面用了一个静态内部类,而后在这个类里面初始化了一个静态对象。