JDK设计模式(一)单例模式

单例模式确保一个类只有一个实例,并提供一个全局访问点。

其类图如下所示。
20150124123552 82620

<!--[endif]-→<!--[if gte mso 9]><![endif]-→

本文主要从饿汉式,懒汉式,懒汉式改进,来讲解单例模式。

1、饿汉式单例

饿汉式单例类是在Java 语言里实现得最为简便的单例类。在类被加载时,就会将自己实例化。

public class Singleton {
    private static Singleton uniqueInstance = new Singleton();
    private Singleton() {
       // Exists only to defeat instantiation.
    }
    public static Singleton getInstance() {
       return uniqueInstance;
    }
    // other methods...
}

2、懒汉式(双重加锁)

通过synchronized关键字,同步不同线程对getInstance()的访问。这就是所谓的懒汉模式。与饿汉式单例类不同的是,懒汉式单例类在第一次被引用时将自己实例化。这种简单实现的问题在于,每次访问getInstance()都需要同步操作,而事实上同步只在第一次访问时有意义。为了避免不必要的同步操作,在JDK1.5以后可以使用一种双重检查加锁的方法。
public class Singleton {
    // volatile is very important for uniqueInstance consistency.
    private volatile static Singleton uniqueInstance = null;
    private Singleton() {
       // Exists only to defeat instantiation.
    }
    public static Singleton getInstance() {
       // first check no need to synchronize.
       if (uniqueInstance == null) {
           // second check need to synchronize, but only run limit times.
           synchronized (Singleton.class) {
              if (uniqueInstance == null) {
                  uniqueInstance = new Singleton();
              }
           }
       }
       return uniqueInstance;
    }
    // Other methods...
}

volatile确保uniqueInstance被初始化为单例后的改变对所有线程可见,多线程能够正确处理uniqueInstance变量。getInstance()中包含两次判空操作,第一次判空每次访问都会执行,而第二次判空只在初始访问存在大量并发的情况下出现。通过两次判空避免了不必要的线程同步。之所以限制必须在JDK1.5后使用是因为,之前的Java存储模型不能保证volatile语义的完全正确实现。

3、懒汉式改进

为了突破这种限制《Effective Java》中给出了一种精妙的解决方法,充分利用了Java虚拟机的特性。

public class Singleton {
    // an inner class holder the uniqueInstance.
    private static class SingletonHolder {
       static final Singleton uniqueInstance = new Singleton();
    }
    private Singleton() {
       // Exists only to defeat instantiation.
    }
    public static Singleton getInstance() {
       return SingletonHolder.uniqueInstance;
    }
    // Other methods...
}

当getInstance方法第一次被调用时,在第一次调用SingletonHolder.uniqueInstance,初始化SingletonHolder类,这种用法的优雅之处在于getInstance方法不需要同步,执行只有一个字段访问,因此惰性初始化对实际的访问没有任何额外的代价。VM同步字段访问,只需要初始化SingletonHolder类,一旦被初始化,后续的字段访问不会涉及到任何判断和同步。

JDK中使用单例模式的有Runtime、NumberFormat等类。

总结

  1. 单件模式确保程序中一个类最多只有一个实例,提供访问实例的全局点。

  2. 在Java中实现单件模式需要私有的构造器,一个静态方法和一个静态变量。

  3. 确定在性能和资源上的限制,使用适当的方案解决多线程问题

  4. 使用多个类加载器,可能会导致单件失效而产生多个实例

文章目录
  1. 1、饿汉式单例
  2. 2、懒汉式(双重加锁)
  3. 3、懒汉式改进
  4. 总结