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

单例模式确保一个类只有一个实例,并提供一个全局访问点。
其类图如下所示。
image.png
本文主要从饿汉式,懒汉式,懒汉式改进,来讲解单例模式。

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. 使用多个类加载器,可能会导致单件失效而产生多个实例
https://alicharles.oss-cn-hangzhou.aliyuncs.com/static/images/mp_qrcode.jpg
文章目录
  1. 1、饿汉式单例
  2. 2、懒汉式(双重加锁)
  3. 3、懒汉式改进
  4. 总结